Skip to content

Instantly share code, notes, and snippets.

@benlinton
Forked from runemadsen/description.markdown
Last active July 12, 2018 14:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benlinton/fd72844978fbdd3a2d90 to your computer and use it in GitHub Desktop.
Save benlinton/fd72844978fbdd3a2d90 to your computer and use it in GitHub Desktop.

Polymorphic Associations reversed

It's pretty easy to do polymorphic associations in Rails: A Picture can belong to either a BlogPost or an Article. But what if you need the relationship the other way around? A Picture, a Text and a Video can belong to an Article, and that article can find all media by calling @article.media

This example shows how to create an ArticleElement join model that handles the polymorphic relationship. To add fields that are common to all polymorphic models, add fields to the join model.

class Article < ActiveRecord::Base
has_many :article_elements
has_many :pictures, :through => :article_elements, :source => :element, :source_type => 'Picture'
has_many :videos, :through => :article_elements, :source => :element, :source_type => 'Video'
end
class Picture < ActiveRecord::Base
has_one :article_element, :as =>:element
has_one :article, :through => :article_elements
end
class Video < ActiveRecord::Base
has_one :article_element, :as =>:element
has_one :article, :through => :article_elements
end
class ArticleElement < ActiveRecord::Base
belongs_to :article
belongs_to :element, :polymorphic => true
end
t = Article.new
t.article_elements # []
p = Picture.new
t.article_elements.create(:element => p)
t.article_elements # [<ArticleElement id: 1, article_id: 1, element_id: 1, element_type: "Picture", created_at: "2011-09-26 18:26:45", updated_at: "2011-09-26 18:26:45">]
t.pictures # [#<Picture id: 1, created_at: "2011-09-26 18:26:45", updated_at: "2011-09-26 18:26:45">]
# STI parent
#
# id :integer not null, primary key
# type :string(255) not null
# title :string(255) not null
# content :text
#
class Post < ActiveRecord::Base
scope :articles, -> { where(type: "Article") }
scope :pages, -> { where(type: "Page") }
end
# STI child (taggable)
#
# Uses posts table.
#
class Article < Post
has_many :taggings, as: :taggable, dependent: :destroy
has_many :tags, through: :taggings
end
# STI child (taggable)
#
# Uses posts table.
#
class Page < Post
has_many :taggings, as: :taggable, dependent: :destroy
has_many :tags, through: :taggings
end
# Independent model (taggable)
#
# id :integer not null, primary key
# title :string(255) not null
# url :string(255) not null
#
class Video < ActiveRecord::Base
has_many :taggings, as: :taggable, dependent: :destroy
has_many :tags, through: :taggings
end
# Many-to-many polymorphic table
#
# id :integer not null, primary key
# tag_id :integer not null
# taggable_id :integer not null
# taggable_type :string(255) not null
#
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :taggable, polymorphic: true
end
# Attach a tag to any model through taggings
#
# id :integer not null, primary key
# title :string(255) not null
#
class Tag < ActiveRecord::Base
has_many :taggings, dependent: :destroy
with_options through: :taggings, source: :taggable do |tag|
tag.has_many :articles, source_type: "Post", class_name: "Article"
tag.has_many :pages, source_type: "Post", class_name: "Page"
tag.has_many :videos, source_type: "Video"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment