I've see that the polymorphic association break the rules of relational db design and in the end cause more harm to the app. so, the left alternative is using STI. someone can please explain how STI works or know if there is another good alternative in using polymorphic association?
thank's
About STI
When you're using STI, you have one parent model which has its own table in DB. And then you have several submodels. But they don't have own tables. Instead their objects are stored in the table of the parent model.
Let's take a look at the example
Generate parent model Vehicle. Add the type column to it. That column will be used for storing the name of the submodel to which belongs corresponding object.
$ rails generate model vehicle type:string color:string price:decimal{10.2}
Then generate submodels telling that Vehicle is their parent model:
$ rails generate model car --parent=Vehicle
$ rails generate model truck --parent=Vehicle
You'll get the following models:
class Car < Vehicle
end
class Truck < Vehicle
end
Now when you create new car it will be stored in the vehicles table and will have 'car' in the type column. Trucks will be stored in the vehicles table too. But they'll have 'truck' in the type column.
About good and bad alternatives.
You know, STI isn't a silver bullet and is considered as pretty bad practice too. For example it can make the table of your parent model the cluttered nightmare. It can prevent you from indexing your data effectively and so on.
Truth is you can use the certain practice when it fits your needs. STI is good when you have mostly the same submodels maybe with slightly different behavior. In that case your table will be neat and your data will be organized naturally and you will not get much harm.
Related
I encountered against the typical case of a model (Address) and multiple models having this data (Company, Person). Let's work with these models as example, but can be generalized with any others.
Making the correct choice is important, as changing the database schema or making deep changes are not easy later.
Initially it will be one-to-one association, then we have these possibilities:
1) Use the classical normalization and put all the address data into each model requiring it. Then create an address subform for render partial into each one, and put the code behavior into a Module to be called from each model controller using it.
This could looks fine, but it has the problem that is hard to make changes, as any change in the Address data needs to be done on each model using it. Also hard to change it to a one-to-many association, if required.
2) Rails polymorphic feature. Address is a model itself, and belongs_to :addressable, polymorphic: true, then Company and Person have has_one/has_many :address/:addresses, as :addressable. Then add the polymorphic FK to Address table and it's done.
I like this solution, it is clear and with only 2 extra columns we have done it. The only contra I can think about is we rely in a framework feature, so if someday we migrate to another one, it should feature polymorphic too. Well this is not really true because we can always make the SQL manually searching by addressable_type = "type", but I think is not database design standard.
3) Join tables. One for each parent. So we have JT_Company_Address, JT_Person_Address, and we use the Rails has_one/has_many :through associations using those join tables.
I think using join tables is more database design standard, but add some extra tables.
4) Reverse the relationship, making Address the parent and each of the other a child. So each child would have a FK address_id.
I don't like much this one, as I will handle mainly companies and persons, and then look up their address if required, not the opposite, looking addresses and then loop up what it has in it. We can always use the bidiriectional association using the Rails inverse_of, but at database layer Address would be the parent one anycase.
My favorites at this moment are 2 and 3. I will probably use Rails and not move from it, so the polymorphic looks very easy and clean.
Then, what do you think, which one is the best one? Any advice is welcome.
Thanks.
I'm creating a logbook app that will work for multiple sports (e.g. scuba-diving and sky-diving) and I'm trying to find a way to have children models inheriting not only parent methods, but parent (database) columns.
An image may explain better:
Logbook database sample
So, any models under the category ScubaDiving, FreeDiving and SkyDiving are models that I will be calling from the views.
The models under Parent or Diving however, will never be called, and are only meant to populate their children with database columns (and methods perhaps).
What is the best way to go about creating the models + associations + inheritance ?
From the research I've done, I've read about:
Single Table Inheritance:
Will only pass parent methods, not database columns
Polymorphic Associations:
I compared my situation to the example I found (comments on photos, articles, posts), but my Divespot and Airport (equivalent: Photo, Article) can't have many Spots (eq: Comments).
Multiple Table Inheritance:
Couldn't make it work, but it seemed the closest to what I was looking for?
Simple models:
I could always create a model for each child (with no parent) and repeat the code. I still wanted to see if there was a way to refacto.
I am using Rails 5 and a PostGresql database.
Single Table Inheritance:
Will only pass parent methods, not database columns
Of course all classes inherit the database columns. Since all classes "live" in the same table, they have the same columns. STI is probably not the best solution in your case for another reason - the columns seem to be different for each model, so you would have a bunch of columns being "NULL" for the models that don't have these columns.
I'd suggest considering to create a model / table for each of your classes and then linking the children objects to an object representing the parent. You can then delegate methods like name, location to the Spot, for example. You will probably then need to work with polymorphic associations. You can be a bit fancy and try "proper" MTI, see this guide
I have a problem with habtm on a single model in rails.
Example:
Let us say i have a User model with two roles "Student" and "teacher". User model is common for two roles. Now
Each student can be associated to many teachers
Each teacher can be associated to many students
In rails notation, their should be habtm between teacher and student
How this can be achieved with single table.
Thanks,
Aashish
It can't be done with a single table. In a many-to-many relationship, no matter what, you always need a table where you store the associations.
In your case, given the association seems to be parent/child, then you just need two tables instead of one.
How to implement it, it depends on your database structure and data organization. You should create an users_users table (as part of the habtm) and configure the references accordingly. If the user table, as it seems to be, is also used for STI, then the configuration may change a little bit.
I've already defined the data model for my application, which will contain this particular part:
Now, my particular issues are related to modelling "Value" and it's child models "Value_Decimal" and "Value_Text". Basically I wan't to have this hierarchy as it's expected to have several other value types, and each value model with have several other different columns (I'm not considering Single Table Inheritance due to this fact).
How can I implement this with Rails' Active Record, can someone point some directions?
Many thanks in advance!
The item_property_value table should have a value_type column allowing for polymorphic associations. Then you can create different tables named for each of your various 'value' models inheriting from an abstract value model (that has no table). This way you can avoid STI, but still inherit from one base model.
Because the item_property_value model is combining 3 different relationships it will need to be treated as a first-class Rails model which means it will also need an id as a primary key. You can then use a has-many-through mapping to access the actual objects it is joining.
Read up here on polymorphic associations. Abstract classes are simply flagged as such:
class Value < ActiveRecord::Base
self.abstract_class = true
end
How do the relationships magically function when only the models are altered?
If I want a "has__and___belongs___to__many" relationship, what should I name the table (so Rails can use it) that contains the two foreign keys?
Short answer: You can't just tell the models that they're related; there have to be columns in the database for it too.
When you set up related models, Rails assumes you've followed a convention which allows it to find the things you wrote. Here's what happens:
You set up the tables.
Following conventions in Rails, you name the table in a particular, predictable way (a plural noun, e.g. people). In this table, when you have a relationship to another table, you have to create that column and name it in another predictable way (e.g. bank_account_id, if you're relating to the bank_accounts table).
You write a model class inheriting from ActiveRecord::Base
class Person < ActiveRecord::Base
When you instantiate one of these models, the ActiveRecord::Base constructor looks at the name of the class, converts it to lowercase and pluralizes it. Here, by reading Person, it yields people, the name of the table we created earlier. Now ActiveRecord knows where to get all the information about a person, and it can read the SQL output to figure out what the columns are.
You add relationships to the model: has_many, belongs_to or has_one.
When you type something like, has_many :bank_accounts, it assumes a few things:
The name of the model that you relate to is BankAccount (from camel-casing :bank_accounts).
The name of the column in the people table which refers to a bank account is bank_account_id (from singularizing :bank_accounts).
Since the relationship is has_many, ActiveRecord knows to give you methods like john.bank_accounts, using plural names for things.
Putting all of that together, ActiveRecord knows how to make SQL queries that will give you a person's bank accounts. It knows where to find everything, because you followed a naming convention that it understands when you created the table and its colums.
One of the neat things about Ruby is that you can run methods on a whole class, and those methods can add other methods to a class. That's exactly what has_many and friends are doing.
This works because you are following "Convention over Configuration".
If you state that a customer model has many orders then rails expects there to be a customer_id field on the orders table.
If you have followed these conventions then rails will use them and will be able to build the necessary SQL to find all the orders for a given customer.
If you look at the development.log file when you are developing your application you will be able to see the necessary SQL being built to select all orders for a given customer.
Rails does not create tables without you asking it to. The creation of tables is achieved by generating a migration which will create/alter tables for you. The fact that you create a customer model and then state within it that it has_many :orders will not create you an orders table. You will need to do that for yourself within a migration to create an orders table. Within that migration you will need to either add a customer_id column or use the belongs_to: customer statement to get the customer_id field added to the orders table.
The rails guide for this is pretty useful