Skip to content

Instantly share code, notes, and snippets.

@runemadsen
Created September 26, 2011 15:23
Show Gist options
  • Save runemadsen/1242485 to your computer and use it in GitHub Desktop.
Save runemadsen/1242485 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">]
@chabgood
Copy link

Polymorphic is a bad idea as there is no way to keep the database consistent due to no FK.

@dorelly2
Copy link

dorelly2 commented Jul 4, 2017

In Picture change it to: has_one :article, :through => :article_element

@dorelly2
Copy link

dorelly2 commented Jul 4, 2017

What is interesting is inventing some kind of a form helper for the Article (to change a Picture to Video etc.)

@WMahoney09
Copy link

WMahoney09 commented Sep 19, 2017

I have the same question as @kbighorse
The issue I have with the answer given by @ayrton is that the result would be an array of ArticleElements, not an array of Pictures and Videos.
I would like to provide an ActiveRecord helper on my Article that reads like Article.first.media that does not bring back a bunch of joins, but reaches through the join and assembles a list of what is on the other side of that polymorphic table, i.e. a mix of Pictures and Videos that are polymorphically related to a given Article
I suppose this could be done by asking for Article.first.pictures and joining that manually with Article.first.videos

This post explains why it is at least tricky if not impossible to traverse a polymorphic :through relationship in the "other" direction

@WMahoney09
Copy link

One option would be

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'

  def media
    self.article_elements.collect{ |el| el.element }
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment