March 25, 2025
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