Rails Single Table Inheritance using Foreign Key (ID) - ruby-on-rails

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

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')

Database functions on RoR ActiveRecord Model layer

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

cloning a model in Rails

I have a model in Rails that I would like to use as a basis for another.
For example model: parent will be the template for model child.
I can see two possible options:
(1) Inherit from the first model and then add additional columns
Class Parent < ActiveRecord::Base
Class Child < Parent
(2) Copy the model.rb file and add new features
Class Child < ActiveRecord::Base
In both cases the "Rails" part of the model is created, but what about the database table? I could create the table using create table child as select * from parent where 1=2 and then create migrations to add the additional columns, but it doesn't seem like the "Rails way".
Is there an easy way to create a migration based on an existing table. or am I barking up the wrong tree entirely?
Your (1) is called single table inheritance (STI). Basically you use one table that has both the fields of the parent and the child. You'll also need a column called type to identify the type of the object.
Without more details I can't say if it's a good idea to use STI in your case, but (2) copying model.rb certainly doesn't seem right.

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.

Rails Generate Model from Existing Table?

I'm very new to the rails framework and want to know how to generate a model based on an existing table. For example, I have a table named person and want to generate the model based on the columns from that table. However, whenever I use "ruby script/generate model Person --skip-migration it creates an empty table named people and creates the model after that. Is there a way to generate a model after a table named person?
Thanks.
Rails is very opinionated, so if you have a table called "person" and you want the corresponding model to be called Person, you need to tell Rails explicitly not to be so clever (otherwise, it will assume that it needs to look for the plural of the model name for the table name).
class Person < ActiveRecord::Base
set_table_name 'person'
end
If your table's primary key isn't called "id", then you'll need to specify that, too...
set_primary_key 'person_id'
You may also need to specify a different autoincrement sequence name, depending on your database.
There's not a way that I know of to automatically generate a model from an existing legacy table, but this should get you most of the way there.

Resources