I have a tag system that can apply to various Taggable
s. I want to be able to query them transparently, as in the accepted solution here:
HABTM Polymorphic Relationship
I want to be able to say, for example, show me all Post
s, Page
s, Video
s, etc. tagged with 'foo'. Pretty straighforward. The complications:
- The different kinds of
Taggable
s are not polymorphic in the OO sense, only through polymorphic associations. There is ataggables
table to guarantee uniqueness of taggable_id (the primary key inposts
, etc.). Tag
s, not surprisingly, have a many-to-many relation withTaggable
s, and are associated through themap_tags
table.
Models:
class Tag < ActiveRecord::Base
has_many :map_tags
has_many :tagged, :through => :map_tags
end
class MapTag < ActiveRecord::Base
belongs_to :tags
belongs_to :tagged, :polymorphic => :true
end
class Post < ActiveRecord::Base
has_many :map_tags, :as => :tagged
has_many :tags, :through => :map_tags
# other taggables are set up similarly
end
Questions:
- I guess my first questions ought to be: is this even possible? I have seen Models "have many" associated instances polymorphically, but I'm not sure if it works the other way, as the error message below may suggest...
- Did I set up the polymorphism correctly? My understanding is that the "child" models basically declare themselves as "tagged", and whichever table interfaces with them directly (in this case
map_tags
) is the one to get the:polymorphic
declaration. - How do I say "show me everything tagged with 'foo'" in ActiveRecord-speak?
What I've tried:
irb> Tag.find_by_name( 'foo', :include => :tagged )
Tag Load (0.1ms) SELECT `tags`.* FROM `tags` WHERE `tags`.`name` = 'foo' LIMIT 1
NameError: uninitialized constant Tag::Tagged
...
irb> Tag.find( :all, :include => :tagged, :conditions => ["Tag.name = 'foo'"] )
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError:
Cannot have a has_many :through association 'Tag#tagged' on the polymorphic object 'Tagged#tagged'.
...
EDIT
I found this post, which points out that I need to specify :source
and :source_type
. So:
class Tag < ActiveRecord::Base
has_many :map_tags
has_many :posts, :through => :map_tags, :source => :tagged, :source_type => 'Post'
has_many :pages, :through => :map_tags, :source => :tagged, :source_type => 'Page'
end
I think I'm closer(?), but I'm still not sure how to query the associated objects...
irb> Tag.first.posts
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` LIMIT 1
NoMethodError: undefined method `posts' for #<Tag id: 1, name: "foo">
irb> Tag.first.tagged
Tag Load (0.2ms) SELECT `tags`.* FROM `tags` LIMIT 1
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError:
Cannot have a has_many :through association 'Tag#tagged' on the polymorphic object 'Tagged#tagged'.