2025-03-25

Polymorphic associations checklist

When you’re unsure to use a polymorphic association:

Does this table belong to multiple, unrelated models?

👍 good use case: a Notification might belong to a Task, Comment, or a User.

👎 bad use case: a Book belongs to an Author…this is a regular belongs_to relationship

Do I need a flexible relationship?

You want a single association that can reference different models dynamically. You don’t want to add a new foreign key each time you need a new type of association.

example: instead of adding post_id, product_id, and event_id to Taggings, you use taggable_type and taggable_id.

Is this relationship likely to grow in complexity over time?

You anticipate that this association will be needed by many different models now or in the future.

example: a Tag might eventually need to attach to a Post, Photo, Video, or Event.

Do I want to avoid schema changes?

You want to avoid altering the schema every time a new model needs to use the association. Polymorphic associations let you add new relationships without additional migrations

When to avoid polymorphic associations

• If the associated models are closely related or known to be limited in number.

• If you need JOIN queries across polymorphic types.
example: since commentable_type varies row-to-row(ex: a Post commentable_type, or a Photo commentable_type) you are unable to use a single SQL JOIN.

• If you need foreign key constraints.

Lets create a polymorphic association!

create migration:

# db/migrate/20240411123456_create_comments.rb
class CreateComments < ActiveRecord::Migration[7.0]
  def change
    create_table :comments do |t|
      t.text :body
      t.references :commentable, polymorphic: true, null: false

      t.timestamps
    end
  end
end

model setup:

# models/comment.rb
class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end
# models/post.rb
class Post < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end
# models/photo.rb
class Photo < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end

usage example:

post = Post.create(title: "First Post")
photo = Photo.create(caption: "Nice view!")

# add comments to each
post.comments.create(body: "Great post!")
photo.comments.create(body: "Awesome picture!")

# fetch all comments
Comment.all.each do |comment|
  puts "#{comment.body} on #{comment.commentable.class.name} ##{comment.commentable.id}"
end