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.

Featured
Level up faster
Recommended Books
Check out my list.
One on Ones: 101
Leveraging Other People's Experience
Hey, I'm Nicholas Dill.

I help people become better software developers with daily tips, tricks, and advice.

Level up
Related Articles
More like this
Ruby Class vs. Instance Methods: What's the Difference?
Understanding Instance Variables in Ruby on Rails
Ruby Templating Engines: ERB vs Haml vs Slim