THE CHALLENGE: How would you do this: has_many :through & polymorphic associations

Hi,

I have a category model and several other models that I want to
categorize.
Categories are organised in a hierarchy.

class Category < ActiveRecord::Base
acts_as_tree
end

class User < ActiveRecord::Base
end

class Image < ActiveRecord::Base
end

I am quite sure that I am going to have additional models (like User
and Images) that I want to categorize later on.
It should be possible to assign a category to more than one object.
For example I have a category “new” and I want to assign this category
to several user and image records.

So many Images and Users could have many categories and vice versa.

I thought maybe it is possible to implement this with a
has_many :through association in combination with polymorphic => true,
but until now I could not fiqure out how to declare this in the models
and how my migrations have to look like…

Thanks for your help.

Feurio

I believe you’ll want to add an addition model for the join table that
uses polymorphic associations like this:

class CategorizedItem < ARec::Base
belongs_to :category
belongs_to :categorizable, :polymorphic=>true
end

class Category < ARec::Base
acts_as_tree
has_many :categoriezed_items

has_many :categorizables, :through=>:categorized_items,
:source=>:categorizable
end

class User < ARec::Base
has_many :categorized_items, :as=>:categorizable # could be has_one
end

class Image < ARec::Base
has_many :categorized_items, :as=>:categorizable # could be has_one
end

You can use the :source option (as in Categories has_many :through) to
identify which association to use from the join table.

HTH,
AndyV

I created the model like described by you above.

For the CategorizedItem model I used the following migration:

class CreateCategorizedItems < ActiveRecord::Migration
def self.up
create_table :categorized_items do |t|
t.references :categorizable, :null => false, :polymorphic =>
true
t.timestamps
end
end

def self.down
drop_table :categorized_items
end
end


Sadly it seems not to work (completly).

c = Category.create
=> #<Category id: 2061879280, name: nil, description: nil, parent_id:
nil, created_at: “2008-03-17 15:16:18”, updated_at: “2008-03-17
15:16:18”>

c.categorizables
ActiveRecord::HasManyThroughAssociationPolymorphicError: Cannot have a
has_many :through association ‘Category#categorizables’ on the
polymorphic object ‘Categorizable#categorizable’.
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/reflection.rb:187:in check_validity!' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/ active_record/associations/has_many_through_association.rb:6:ininitialize’
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/associations.rb:1032:in new' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/ active_record/associations.rb:1032:incategorizables’
from (irb):4

The following seems to work, but how would I assign a Category to a
Uses?

u = User.create
=> #<User id: 2, created_at: “2008-03-17 15:17:27”, updated_at:
“2008-03-17 15:17:27”>

u.categorized_items
=> []

Thanks,

Feurio

You’re missing the ‘category’ half of the references:

class CreateCategorizedItems < ActiveRecord::Migration
def self.up
create_table :categorized_items do |t|
t.references :categorizable, :null => false, :polymorphic =>true
t.references :category, :null => false
t.timestamps
end
end

def self.down
drop_table :categorized_items
end
end

use the has_many_polymorphs plugin

Sorry, still not working…

c = Category.find(943302162)
=> #<Category id: 943302162, name: “computer_science”, description:
“Informatik”, parent_id: 2061879279, created_at: “2008-03-18
09:53:21”, updated_at: “2008-03-18 09:53:21”>

c.categorizables
ActiveRecord::HasManyThroughAssociationPolymorphicError: Cannot have a
has_many :through association ‘Category#categorizables’ on the
polymorphic object ‘Categorizable#categorizable’.
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/reflection.rb:187:in check_validity!' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/ active_record/associations/has_many_through_association.rb:6:ininitialize’
from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/
active_record/associations.rb:1032:in new' from /usr/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/ active_record/associations.rb:1032:incategorizables’
from (irb):9

What I have now…

The migration files:

class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :name, :description
t.integer :parent_id
t.timestamps
end
end

def self.down
drop_table :categories
end
end

class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|

  t.timestamps
end

end

def self.down
drop_table :users
end
end

class CreateCategorizedItems < ActiveRecord::Migration
def self.up
create_table :categorized_items do |t|
t.references :categorizable, :null => false, :polymorphic =>
true
t.references :category, :null => false
t.timestamps
end
end

def self.down
drop_table :categorized_items
end
end

The models:

class Category < ActiveRecord::Base
acts_as_tree
has_many :categorized_items
has_many :categorizables, :through => :categorized_items, :source
=> :categorizable
end

class User < ActiveRecord::Base
has_many :categorized_items, :as=>:categorizable # could be has_one
end

class CategorizedItem < ActiveRecord::Base
belongs_to :category
belongs_to :categorizable, :polymorphic=>true
end


And how would I do something like the following with the associations
above?

c = Category.create
u = User.create
u.categories << Category

Thanks for your patience!

Feurio

Hi,

thanks everybody for the help.
I got it working now with the plugin has_many_polymorphs.

Would have been nice to get it working with standard rails, too. But
also I was googling and used some reference books like the rails way
or agile webdevelopment with rails I could find a working solution…

Feurio