1

I have a tag system that can apply to various Taggables. 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 Posts, Pages, Videos, etc. tagged with 'foo'. Pretty straighforward. The complications:

  1. The different kinds of Taggables are not polymorphic in the OO sense, only through polymorphic associations. There is a taggables table to guarantee uniqueness of taggable_id (the primary key in posts, etc.).
  2. Tags, not surprisingly, have a many-to-many relation with Taggables, and are associated through the map_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:

  1. 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...
  2. 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.
  3. 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'.
Community
  • 1
  • 1
acobster
  • 1,637
  • 4
  • 17
  • 32

1 Answers1

1

Your polymorphic association setup is correct. Except for a small typo :

belongs_to :tagged, :polymorphic => :true

It should be true the boolean not :true the symbol !

charlysisto
  • 3,700
  • 17
  • 30