Writing Migrations for Polymorphic Associations in Ruby on Rails
When you want to create an association between a model and 2 or more other models, you need to create a polymorphic association.
Instead of a traditional association where an Image might belong to a Profile (shown below), a polymorphic association would allow images to belong to Profile, Blog Posts, and anything else that needs an image.
class Image < ApplicationRecord
belongs_to :profile
end
class Profile < ApplicationRecord
has_many :images
end
To make this reference polymorphic, just a few changes are needed:
class Image < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Profile < ApplicationRecord
has_many :images, as: :imageable
end
We make the belongs_to
call with a reference to :imageable
since our Image will belong to multiple models. And instead of storing separate ids, like profile_id
to point to profiles and blog_post_id
to point to a blog post, we combine the ids into a shared id we call imageable_id
.
To determine which table this id refers to, we also store a imageable_type
that tells us the name of the model.
Writing Polymorphic Association Migrations
When we want to create a polymorphic table, we need to change a few things in our migrations.
Using the example above, our Image model can belong to either a Profile or a BlogPost. Instead of storing a profile_id
or blog_post_id
we store an id that can point to either called imageable_id
. In order to determine which id the model points to we reference the imageable_type
.
As illustrated above, we need 2 fields to make a polymorphic association:
- id of the associated model
- type of the model
Creating a Table With a Polymorphic Association
When we create a table we can either manually create these columns or use a shorthand helper method.
The manual approach would look like this and clearly lay out the columns we want:
class CreateImages < ActiveRecord::Migration[6.0]
def change
create_table :images do |t|
t.bigint :imageable_id
t.string :imageable_type
end
add_index :images, [:imageable_type, :imageable_id]
end
end
Or we can use the shorthand references
method. It doesn't explicitly show us all the columns being created but it's a little easier to work with.
class CreateImages < ActiveRecord::Migration[6.0]
def change
create_table :images do |t|
t.references :imageable, polymorphic: true
end
end
end
Both approaches will result in the same change to your database.
Adding a Polymorphic Column to an Existing Table
You can use the add_reference
method If you already created the table and just need to add a column,
Here's what it would look like:
class AddImageableToImages < ActiveRecord::Migration
def change
add_reference :images, :imageable, polymorphic: true, index: true
end
end
This will do the same as all the other variants above, creating imageable_id
and imageable_type
fields.