Polymorphic Associations allow you to create a single model and let it “belong to” multiple models. Allow me to explain how they work.
I wanted to have a single
Photo model, which can be used for thumbnails, employees, product, etc. for the practice management app that I am working on.
I knew that making a different model for each of these is not feasible, and it will lead to a lot of duplicated code, so I implemented them using Polymorphic associations.
Rails Polymorphic Associations
Well, with polymorphic associations, a model can belong to more than one other model, on a single association.
For example, you might have a picture model that belongs to either an employee model or a product model (visualized in the pic above). Don’t worry if you don’t understand the code written there. You will after reading this article (probably).
The most weird part of the above code seems to be the part which says
belongs_to :imageable, polymorphic: true
This just means that the model Picture does not belong to any of the Employee or Product model but to something called
Uh, oh. I was taught that models belong to models and nothing else. You can check any code which has belongs_to clause.
Well, that is kinda true, but we have to change it a little while using polymorphic associations (if you forgot, they mean that it could be associated with many models). So, imageable here acts as an interface to link to, for other models which link the same way they did except now they add a clause of
as: :imageable. For example, the Person model now looks like:
class Person < ActiveRecord::Base has_many :pictures, as: :imageable end
You could have used any word in place of imageable, like picturable, photogenic, photograph, baklind, dhoni anything! They just have to match your column name.
Wait, what? Which column? What column? Which table?
Good question. You have to setup a reference in your Picture model to store the foreign key of the row it
belongs_to. Now how do we do that?
First, let us look at how we did for non-polymorphic associations. If, for example we have a Question model and Choice model, we say choice belongs to question and question has many choices. So, to make that thing work properly, we generally include a column
question_id in the
choices table. That thing generally works out without any problem and we live a happy satisfied life.
But, there is no specific model assigned here in the polymorphic associations. What field do we use then? A wise man said, double the amount if you are not sure. Lets follow that wise man. We use two foreign keys! One key for the
id and another for
For the Picture case, they foreign keys are
imageable_id is the id of the record this row belongs to and
imageable_type is the class name. The migration looks something like this,
add_column :pictures, :imageable_id, :integer add_column :pictures, :imageable_type, :string
or you can use a handy trick provided by rails,
t.references :imageable, polymorphic: true
Just run the migration now, add the snippets to your model, and congratulations. You successfully have configured a polymorphic association!
Yayy!! Well that was simple. Thanks man. Now, how do I actually reference it?
Ahh, the easy part. Let me assume that you know how to get data from model ( person = Person.find(23) ). There are two ways to create a reference, from the parent side or from child side.
person.picture = picture
picture.imageable = person
That is all really is there to assigning. Don’t forget to call save method on picture though, otherwise its gone as soon as the object stops persisting.
Seems simple enough, but I have a question. How do you use it to accept data from forms?
This is tricky and so I think I should leave it to experts. Check out this Railscasts. It shows how to do all this for comments. Ideal if you wish to create Facebook and would need comments for all your Photo, Status, Video models.
You can either use the method shown by Ryan Bates in Railscasts or do manual assigning the way I showed. I prefer doing the manual assigning though. Easier to setup and less confusing. Plus, you have full control as you know where the code is and what it does.
I have one little trick which I’d like to share. As many people know, adding index to column in tables makes it for faster searching for data through that column, you all will be tempted to add index to both
imageable_id columns by typing two migrations like this
add_index :pictures, :imageable_id add_index :pictures, :imageable_type
add_index :pictures, [:imageable_type, :imageable_id]
imageable_typecolumn. We are storing it as a string but do you think we would really have a model name of 255 characters? Decreasing the size will also allow for smaller indexing and faster searching. We can limit the size by passing an option
add_column :pictures, :imageable_type, :string, limit: 15
Have some of your little tricks or you think I did a mistake and a proved to be a bad teacher? Write it in the comments.