I have a many to many association setup in Rails with ActiveRecord. Let's say the tables are
+------------+ +----------------+ +----------+
| categories | -- has many -= | category_items | =- has many -- | products |
+------------+ +----------------+ +----------+
In the category_items table I have the standard set of id's plus an extra attribute called "type":
id:int
category_id:int
product_id:int
category_type:string
Thankfully, Rails provides some great helpers for making assignment in the mapping table a breeze. For example:
p = Product.first # p.id => 1
c开发者_开发知识库 = Category.first # c.id => 1
# now to make the assignment
p.categories << c
# or
p.categories.create(c)
That's all well and good, but let's say I want to automatically update the "type" field with the class name of the other table. So for the examples given above, my category_items row would look like this:
id = 1 (or some number)
category_id = 1
product_id = 1
category_type = nil
But I would like category_type to equal "Product". Is there a way for me to build a callback or define something in the association that would automatically set the category_type field? I know in polymorphic associations, you can use something like :source or :as to do something like this, but polymorphic associations in many to many associations throws an error.
Any ideas?
Thanks!
Have you tried a polymorphic on category_items like this:
class CategoryItem < ActiveRecord::Base
belongs_to :category, :polymorphic => true
belongs_to :product
end
class ACategory < ActiveRecord::Base
has_many :category_items, :as => :category
end
class AnotherCategory < ActiveRecord::Base
has_many :category_items, :as => :category
end
class Product < ActiveRecord::Base
has_many :category_items
end
I think this will do the trick!
I ended up making separate getter/setter methods. In your case, methods on Product:
setter
def set_category_type(category_id, type)
category_items = self.category_items.select { |category_item| category_item.category_id == category_id}
if category_items.length > 0
category_item = category_items.first
category_item.type = type
category_item.save
else
CategoryItem.create(:product_id => self.id, :category_id => category_id, :type => type)
end
end
getter
def category_type(category_id)
category_item = self.category_items.where(:category_id => category_id ).first()
if category_item.nil?
nil # or some other default type value
else
category_item.type
end
end
This was basically what I needed but couldn't find a more elegant way. Please show me a better way if you know.
精彩评论