has_many:通过has_and_belongs_to_many关联

时间:2010-01-27 21:58:26

标签: ruby-on-rails activerecord

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

class FoodItem < ActiveRecord::Base
  has_and_belongs_to_many :food_categories
  has_many :places, :through => :food_categories
end

class FoodCategory < ActiveRecord::Base
  has_and_belongs_to_many :food_items
  belongs_to :place
end

class Place < ActiveRecord::Base  
  has_many :food_categories
  has_many :food_items, :through => :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_item_id表中查找food_categories

4 个答案:

答案 0 :(得分:7)

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

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

希望这有帮助!

更新:这个问题最近出现了几次。我写了一篇文章nesting your has_many :through relationships来详细解释。它甚至在GitHub上有一个附带的示例应用程序来下载和播放。

答案 1 :(得分:2)

几个月前我写了an article about this。简而言之,Rails不允许has_manyhas_and_belongs_to_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方法。

答案 2 :(得分:1)

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

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

答案 3 :(得分:-1)

这是正确的,因为你无法在连接表上执行“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 - &gt; has_many:food_items”中修正了拼写错误。这应该照顾您的需求,并为您提供额外的好处,即能够在将来为您的FoodCategoryItems“加入”模型添加功能。