has_many:通过has_and_belongs_to_many关联

我试图在Ruby on Rails项目中执行以下操作:

class FoodItem  :food_categories end class FoodCategory < ActiveRecord::Base has_and_belongs_to_many :food_items belongs_to :place end class Place  :food_category end 

但调用实例方法some_food_item.places会给我以下错误:

 ActiveRecord::StatementInvalid: PGError: ERROR: column food_categories.food_item_id does not exist LINE 1: ...laces".id = "food_categories".place_id WHERE (("food_cate... : SELECT "places".* FROM "places" INNER JOIN "food_categories" ON "places".id = "food_categories".place_id WHERE (("food_categories".food_item_id = 1)) 

这很有道理 – 因为FoodItem和FoodCategory上的HABTM我有一个名为food_categories_food_items的映射表。

我需要做些什么来让some_food_item.places通过映射表正确查找位置,而不是在food_categories表中查找food_categories

我的第一个答案版本不正确,但这个版本完美无缺。 我第一次做了一些拼写错误(实际上没有创建应用程序进行测试的危险),但这次我validation了。 并且需要一个插件,但这很容易。 首先,安装插件:

 script/plugin install git://github.com/ianwhite/nested_has_many_through.git 

这将安装Ian White的解决方法,并且无缝地工作。 现在模型,直接从我设置的测试应用程序复制,以使其工作:

 class FoodItem < ActiveRecord::Base has_many :food_category_items has_many :food_categories, :through => :food_category_items has_many :places, :through => :food_categories end class FoodCategory < ActiveRecord::Base has_many :food_category_items has_many :food_items, :through => :food_category_items belongs_to :place end class FoodCategoryItem < ActiveRecord::Base belongs_to :food_item belongs_to :food_category end class Place < ActiveRecord::Base has_many :food_categories has_many :food_category_items, :through => :food_categories has_many :food_items, :through => :food_category_items end 

现在“远”协会的工作也同样如此。 place_instance.food_itemsfood_item.places都可以完美地工作,以及更简单的协会。 仅供参考,这是我的架构,以显示所有外键的位置:

 create_table "food_categories", :force => true do |t| t.string "name" t.integer "place_id" t.datetime "created_at" t.datetime "updated_at" end create_table "food_category_items", :force => true do |t| t.string "name" t.integer "food_item_id" t.integer "food_category_id" t.datetime "created_at" t.datetime "updated_at" end create_table "food_items", :force => true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end create_table "places", :force => true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end 

希望这可以帮助!

更新:这个问题最近出现了几次。 我写了一篇文章, 嵌套你的has_many:通过关系 ,详细解释。 它甚至在GitHub上有一个附带的示例应用程序来下载和玩。

几个月前,我写了一篇关于此的文章 。 简而言之,Rails不允许通过has_and_belongs_to_many关联has_many 。 但是,您可以通过执行以下操作来部分模拟关系:

 class FoodItem < ActiveRecord::Base has_and_belongs_to_many :food_categories named_scope :in_place, lambda{ |place| { :joins => :food_categories, :conditions => {:food_categories => {:id => place.food_category_ids}}, :select => "DISTINCT `food_items`.*" # kill duplicates } } end class FoodCategory < ActiveRecord::Base has_and_belongs_to_many :food_items belongs_to :place end class Place has_many :food_categories def food_items FoodItem.in_place(self) end end 

这将为您some_food_item.places您寻找的some_food_item.places方法。

我正在使用Rails 3.2.13和Rasmus,你的原始设置现在似乎在HABTM上正常工作。

我建议用户在尝试解决方法之前先尝试一下。

这是正确的,因为你不能在连接表上执行“has many through”。 从本质上讲,你试图将关系扩展到比你真正的程度更远的程度。 HABTM(has_and_belongs_to_many)不是解决大多数问题的非常强大的解决方案。

在您的情况下,我建议添加一个名为FoodCategoryItem的模型,并重命名您的连接表以匹配。 您还需要添加主键字段。 然后设置您的模型关联,如下所示:

 class FoodItem < ActiveRecord::Base has_many :food_categories, :through => :food_category_items has_many :places, :through => :food_categories end class FoodCategory < ActiveRecord::Base has_many :food_items, :through => :food_category_items belongs_to :place end class FoodCategoryItems < ActiveRecord::Base belongs_to :food_item belongs_to :food_category end class Place < ActiveRecord::Base has_many :food_categories has_many :food_items, :through => :food_categories end 

注意,我还在“Place – > has_many:food_items”中修正了一个拼写错误。 这应该照顾您的需求,并为您提供额外的好处,以便将来能够为您的FoodCategoryItems“加入”模型添加function。