has_many: через ассоциацию has_and_belongs_to_many - PullRequest
6 голосов
/ 28 января 2010

Я пытаюсь сделать следующее в проекте 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))

Что имеет смысл - из-за HABTM в FoodItem и FoodCategory у меня есть таблица сопоставления с именем food_categories_food_items.

Что мне нужно сделать, чтобы some_food_item.places правильно просматривал места в таблице сопоставления вместо того, чтобы искать food_item_id в таблице food_categories?

Ответы [ 4 ]

7 голосов
/ 28 января 2010

Моя первая версия ответа была неверной, но эта работает отлично. Я сделал несколько опечаток в первый раз (опасность не создать приложение для тестирования), но на этот раз я проверил. И плагин нужен, но это легко. Сначала установите плагин:

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

Это устанавливает обходной путь Яна Уайта и работает без проблем. Теперь модели, скопированные непосредственно из тестового приложения, которые я настроил, чтобы это работало:

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_items и food_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, который можно загрузить и поиграть.

2 голосов
/ 28 января 2010

Несколько месяцев назад я написал статью об этом . Короче говоря, связка has_many через has_and_belongs_to_many не разрешена Rails. Однако вы можете частично смоделировать отношения, выполнив что-то вроде этого:

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, который вы ищете.

1 голос
/ 31 июля 2013

Я использую Rails 3.2.13 и Rasmus, теперь кажется, что ваша оригинальная установка отлично работает на HABTM.

Я бы посоветовал пользователям попробовать сначала, прежде чем пытаться обойти эту проблему.

0 голосов
/ 28 января 2010

Это правильно, потому что вы не можете выполнить «имеет много сквозных» в соединительной таблице.По сути, вы пытаетесь продлить отношения на один градус дальше, чем можете.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 в будущем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...