Database functions on RoR ActiveRecord Model layer - ruby-on-rails

I have a model called "Provision" with a table name "provisions". This model have a attributes with default values in database, because I don't know how to set value through ActiveRecord.
But the great problem is when the default value is null on database and I need to use database function like "GETDATE()" in RoR layer.
I'm wrong in my proporsal?

If your problem has to do with queries, then manipulating the ActiveRecord::Base objects wont be helpful. You can always execute custom SQL with ActiveRecord::Base.connection('SQL STRING').
Edit:
Seems you want to run this on the server: UPDATE provinsions SET provisioned_at = GETDATE() WHERE id = ?. Well that's a piece of cake using ActiveRecord, you can refer to the docs.
# if you don't already have an AR::Base class with the same name (singularized) as your db table create it
class Provision < ActiveRecord::Base
# this is not necessary, it specifies the db table name
# that AR already inferred from the class name
self.table_name = 'provisions'
end
Provision.where(provisioned_at: nil).update_all provisioned_at: Time.zone.now

Related

Create a model based on a query instead of a table

As a first approximation, I would like to know if it would work to define a rails model based on a database view instead of a real table.
Of course, I want to use that model only to query and not to insert or update.
If that's possible, is it necessary to define the view? Can I instead specify somehow a query (so I do not need to actually create the view).
Considering the view already exists and the database.yml configuration is set correctly (i.e. the view is somehow inside the database you defined there) and its name is View.
The model:
# view.rb
def View < ApplicationRecord
self.primary_key = 'id'
end
Then you can query like:
View.where(column_name: 'value')

Rails / ActiveRecord - Single Table Inheritance - overriding type field

Is it possible to override the name of this colummn? I'm changing some parts of my applications to use STI and there are other fields in use for. I would also prefer it to be of type integer.
Any ideas?
According to the code in ActiveRecord::ModelSchema (3.2), the set_inheritance_column method is now deprecated and you should use self.inheritance_column = column
In modern Rails, you'd use inheritance_column= (as panckreous noted):
class M < ApplicationRecord
self.inheritance_column = 'whatever'
#...
end
In older versions of Rails (i.e. what was around when this answer was originally written), you'd use [set_inheritance_column] to change the name:
Sets the name of the inheritance column to use to the given value, or (if the value is nil or false) to the value returned by the given block.
The column still has to be a string (or text) as AR will want to put the class name in there:
Single table inheritance
Active Record allows inheritance by storing the name of the class in a column that is named “type” by default.

next available record id

#user = User.new
#user.id returns nil but i need to know it before i save. Is it possible ?
YES you can!
I had the same question and investigated the docs.
The ability to solve this question is very related to your database type in fact.
Oracle and Postgresql do have useful functions to easily solve this.
For MySQL(oracle) or SkySQL(open-source) it seems more complicated (but still possible). I would recommend you avoid using these (MySQL/SkySQL) databases if you need advanced database tools.
First you must try to avoid this situation as much as possible in your application design, as it is dangerous to play with IDs before they get saved.
There may be situation where you don't have any other choice:
For instance when two tables are referencing themselves and for security reason you don't allow DELETE or UPDATE on these tables.
When this is the case, you can use the (PostgreSQL, Oracle) database nextval function to generate the next ID number without actually inserting a new record.
Use it in conjunction with the find_by_sql rails method.
To do this with postgreSQL and Rails for instance, choose one of your rails models and add a class method (not an instance method!).
This is possible with the "self" word at the beginning of the method name.
self tells Ruby that this method is usable only by the class, not by its instance variables (the objects created with 'new').
My Rails model:
class MyToy < ActiveRecord::Base
...
def self.my_next_id_sequence
self.find_by_sql "SELECT nextval('my_toys_id_seq') AS my_next_id"
end
end
When you generate a table with a Rails migration, by default Rails automatically creates a column called id and sets it as the primary key's table. To ensure that you don't get any "duplicate primary key error", Rails automatically creates a sequence inside the database and applies it to the id column. For each new record (row) you insert in your table, the database will calculate by itself what will be the next id for your new record.
Rails names this sequence automatically with the table name append with "_id_seq".
The PostgreSQL nextval function must be applied to this sequence as explained here.
Now about find_by_sql, as explained here, it will create an array containing new objects instances of your class. Each of those objects will contain all the columns the SQL statement generates. Those columns will appear in each new object instance under the form of attributes. Even if those attributes don't exist in your class model !
As you wisely realized, our nextval function will only return a single value.
So find_by_sql will create an array containing a single object instance with a single attribute.
To make it easy to read the value of this very attribute, we will name the resulting SQL column with "my_next_id", so our attribute will have the same name.
So that's it. We can use our new method:
my_resulting_array = MyToy.my_next_id_sequence
my_toy_object = my_resulting_array[0]
my_next_id_value = my_toy_object.my_next_id
And use it to solve our dead lock situation :
my_dog = DogModel.create(:name => 'Dogy', :toy_id => my_next_id_value)
a_dog_toy = MyToy.new(:my_dog_id => my_dog.id)
a_dog_toy.id = my_next_id_value
a_dog_toy.save
Be aware that if you don't use your my_next_id_value this id number will be lost forever. (I mean, it won't be used by any record in the future).
The database doesn't wait on you to use it. If somewhere at any time, your application needs to insert a new record in your my_table_example (maybe at the same time as we are playing with my_next_id_sequence), the database will always assign an id number to this new record immediately following the one you generated with my_next_id_sequence, considering that your my_next_id_value is reserved.
This may lead to situations where the records in your my_table_example don't appear to be sorted by the time they were created.
No, you can't get the ID before saving. The ID number comes from the database but the database won't assign the ID until you call save. All this is assuming that you're using ActiveRecord of course.
I had a similar situation. I called the sequence using find_by_sql on my model which returns the model array. I got the id from the first object of the arry. something like below.
Class User < ActiveRecord::Base
set_primary_key 'user_id'
alias user_id= id=
def self.get_sequence_id
self.find_by_sql "select TEST_USER_ID_SEQ.nextval as contact_id from dual"
end
end
and on the class on which you reference the user model,
#users = User.get_sequence_id
user = users[0]
Normally the ID is filled from a database sequence automatically.
In rails you can use the after_create event, which gives you access to the object just after it has been saved (and thus it has the ID). This would cover most cases.
When using Oracle i had the case where I wanted to create the ID ourselves (and not use a sequence), and in this post i provide the details how i did that. In short the code:
# a small patch as proposed by the author of OracleEnhancedAdapter: http://blog.rayapps.com/2008/05/13/activerecord-oracle-enhanced-adapter/#comment-240
# if a ActiveRecord model has a sequence with name "autogenerated", the id will not be filled in from any sequence
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
alias_method :orig_next_sequence_value, :next_sequence_value
def next_sequence_value(sequence_name)
if sequence_name == 'autogenerated'
# we assume id must have gotten a good value before insert!
id
else
orig_next_sequence_value(sequence_name)
end
end
end
while this solution is specific to Oracle-enhanced, i am assuming the other databases will have a similar method that you could redefine.
So, while it is definitely not advised and you want to be absolutely sure why you would not want to use an id generated by a sequence, if it is needed it is most definitely possible.
It is why I love ruby and Ruby on Rails! :)
In Oracle you can get your current sequence value with this query:
SELECT last_number FROM user_sequences where sequence_name='your_sequence_name';
So in your model class, you can put something like this:
class MyModel < ActiveRecord::Base
self.sequence_name = 'your_sequence_name'
def self.my_next_id_sequence
get_data = self.find_by_sql "SELECT last_number FROM user_sequences where sequence_name='your_sequence_name'"
get_data[0].last_number
end
end
And finally, in controller you can get this value with this:
my_sequence_number = MyModel.my_next_id_sequence
So, there is no need to get your next value by using NEXTVAL and you won't lose you ID.
What you could do is User.max(id). which will return the highest ID in the database, you could then add 1. This is not reliable, although might meet your needs.
Since Rails 5 you can simply call next_sequence_value
Note: For Oracle when self.sequence_name is set, requesting next sequence value creates side effect by incrementing sequence value

constant enums in rails models and database

I want to find a clean way to maintain enumerations in both the database and model.
Say I have an ActiveRecord class with a constant enumeration of types
class Foo < ActiveRecord::Base
TYPES = {1 => :hello, 2 => :hi}
end
I've gone further and written an Enumeration class so I can do Foo::TYPES.HELLO to get 1, or Foo::TYPES.HI to get 2.
I want these types in the database so I can do joins. I'm currently creating a FooType model, and having Foo belongs_to :foo_type, so Foo will have a foo_type_id field.
class Foo < ActiveRecord::Base
belongs_to :foo_type # 1 -> hello, 2 -> hi
end
However this is inconvenient because:
Test environments break unless I seed the test db every time. Types are assumed to be constant so they are directly used in code. This is probably the biggest pain
If I do a static enumeration pulling foo_type_ids from the db into the model, this also breaks in tests
If I add a new type, I have to reflect this in every database
Each environment needs to be seeded with types
I used the enumerated_attribute gem from https://github.com/jeffp/enumerated_attribute
This lets you define the enumeration and its values in the model. It's really designed to make dealing with them easier on the Rails / UI side (not so much database), but it will save you the hassles of maintaining a separate model for your enumeration and ensure consistency.
It will work in test environments, you won't need to update your DB when you add a new value and you won't need to seed the database. Just create a new column for the value of the enumeration and the gem does the rest. It stores the value as a string so it's pretty easy to manipulate from within the database if that's what you need.
Having enumerators in your code just make your code readable, nothing other than that. Rather than having an integer (which doesn't have readability for developers), you use come kind of String to represent that value
TYPES = {1 => :hello, 2 => :hi}
I dont think you can use AR relations like 'belongs_to' inside a normal class (non AR)
I think what you need is two tables
table -
foos
id
type
foo_type_id
foo_types
id
foo_type
then U can have the AR
class Foo < ActiveRecord::Base
belongs_to :foo_type
end
class FooType < ActiveRecord::Base
has_many : foos
end
If you are going for DB solution (other than having static enumerators), you dont have an option but to add types to each env.
HTH
cheers
sameera

Rails Single Table Inheritance using Foreign Key (ID)

I have to model an association structure and the association is divided into divisions/subdivisions/sections etc. So I've created a simple Entity-Attribute Model:
I'd like to use rail's single-table-inheritance but it seems like this works only if the type column is a string. My question is how to achieve this with my approach? Since I'm using a foreign key as "type" I'd have to query the "type name" first. Has anybody done this before?
I would recommend adding a String "type" attribute to your structure table to satisfy single table inheritance, and to add before_save callbacks to set correct values on either table.
Say you have a StructureType with name "Basic". In Rails that means you'd want to have class hierarchy:
Structure < ActiveRecord::Base
BasicStructure < Structure
In Structure class add:
before_create :set_structure_type_fk
def set_structure_type_fk
self.structure_type = StructureType.find_by_name(\
self.class.name.gsub(/Structure/, '').downcase)
end
Hope this helps.
Note that this approach means that StructureType.name should be immutable: once created it should never be changed, except by a database migration that updates both tables correspondingly.
K

Resources