Skip to content

Instantly share code, notes, and snippets.

@huobazi
Forked from runemadsen/description.markdown
Created February 15, 2017 04:37
Show Gist options
  • Save huobazi/d0012eeedf1b7b45e39e7b21dc3674d9 to your computer and use it in GitHub Desktop.
Save huobazi/d0012eeedf1b7b45e39e7b21dc3674d9 to your computer and use it in GitHub Desktop.
Reverse polymorphic associations in Rails

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">]
@huobazi
Copy link
Author

huobazi commented Mar 28, 2017

Here's an example that uses STI:

# 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