I am trying to do the following in a Ruby on Rails project:
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
But calling the instance method some_food_item.places
gives me the following error:
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))
Which makes perfect sense - because of the HABTMs on FoodItem and FoodCategory I have the mapping table named food_categories_food_items
.
What do I have to do to get some_food_item.places
to look places up correctly through the mapping table instead of looking for a food_item_id
in the food_categories
table?
My first version of the answer was incorrect, but this one works perfectly. I made a couple of typos the first time (the hazard of not actually creating an app to test) but this time I verified. And a plugin is needed, but this is easy. first, install the plugin:
script/plugin install git://github.com/ianwhite/nested_has_many_through.git
This installs Ian White's workaround, and it works seamlessly. Now the models, copied directly from the test app I setup to get this working:
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
Now "far" associations work just as well. place_instance.food_items
and food_item.places
both work flawlessly, as well as the simpler associations involved. Just for reference, here's my schema to show where all the foreign keys go:
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
Hope this helps!
UPDATE: This question has come up a few times recently. I wrote an article, nesting your has_many :through relationships, to explain in detail. It even has an accompanying example application on GitHub to download and play around with.
A few months ago I wrote an article about this. In short, has_many
through a has_and_belongs_to_many
association is not allowed by Rails. However, you can partly simulate the relationship by doing something like this:
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
This will give you the some_food_item.places
method you seek.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With