Skip to content

Instantly share code, notes, and snippets.

@seancdavis
Last active January 12, 2022 14:06
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save seancdavis/e76e6649267655ebc461 to your computer and use it in GitHub Desktop.
Save seancdavis/e76e6649267655ebc461 to your computer and use it in GitHub Desktop.
Rails has_many :through Polymorphic Association (http://goo.gl/lxmehk)
# app/models/image.rb
class Image < ActiveRecord::Base
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
# app/models/post.rb
class Post < ActiveRecord::Base
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
# app/models/tag.rb
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, :through => :taggings, :source => :taggable,
:source_type => 'Post'
has_many :images, :through => :taggings, :source => :taggable,
:source_type => 'Image'
end
# app/models/tagging.rb
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :taggable, :polymorphic => true
end
@seancdavis
Copy link
Author

This is a specific example of how to make a has_many, :through polymorphic association among tags, posts and images, while maintaining the ability to call something like Tag.first.posts.

@gaetanm
Copy link

gaetanm commented Feb 22, 2016

When I type Tag.first.recipes I have the following error:

SELECT "tags".* FROM "tags" ORDER BY "tags"."id" DESC LIMIT 1
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :taggable in model Tagging. Try 'has_many :recipes, :through => :taggings, :source => '. Is it one of tag or tagging?

I have exactly the same code as you except that my model is called Recipe instead of Post. Any Idea?

@seancdavis
Copy link
Author

Good catch, @gaetanm. It was a stupid typing error.

The Tagging polymorphic association should be:

belongs_to :taggable, :polymorphic => true

I've updated the code above, and I'll get the article straightened out. Thank you for addressing.

@amitsaxena
Copy link

Just like @tag.posts and @tag.images, is @tag.all_items possible, where all_items is an array of posts and images? I do not want to do 2 queries and add the 2 arrays, but I need the response in 1 query. Let's say I need top ten entries, then fetching them separately and adding them gets tricky.

@seancdavis
Copy link
Author

From what I could find (specifically, this article), Rails 4 doesn't make this easy because you need to explicitly state each source type on a HMT polymorphic model.

As you've mentioned, one alternative is to query separately and combine the two arrays.

Another potential workaround is to use single table inheritance, but you'll want to make sure you have enough in common between the two models to make it a valid candidate for STI.

@amitsaxena
Copy link

I don't think a straightforward solution for this exists. Probably I'll need to change the database schema to account for this.

@devanmoylan
Copy link

Nice work and thank you for posting. I'm curious, what's the tagging migration and schema look like?

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