A self running template with all models to test by yourself is available in this github gist - run it and it triggers the error.
To visualize it, the structure looks like this:
Colors
|n|
|:|
|1| -----
Houses n:n Conditions
|n| -----
|:|
|1|
People
Starting from the blank db I create some test data (console commands, return values omitted to keep it clear):
irb(main):001:0> Condition.create(condition: :damaged)
irb(main):002:0> house = House.create(conditions: [Condition.first])
irb(main):003:0> person = Person.create
irb(main):004:0> house.person = person
irb(main):005:0> house.save
So now I have some test data. Let's retrieve the person's houses (which are only the damaged ones by definition):
irb(main):006:0> person.damaged_houses
House Load (0.2ms)
SELECT "houses".* FROM "houses"
INNER JOIN "conditions_houses" ON "conditions_houses"."house_id" = "houses"."id"
INNER JOIN "conditions" ON "conditions"."id" = "conditions_houses"."condition_id"
WHERE "houses"."person_id" = ? AND "conditions"."condition" = 'damaged'
[["person_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<House id: 1, person_id: 1>]>
All good, the damaged house is returned and the sql joined the conditions
table correctly. Now I want to get all colors of the person, which is defined as all colors of the houses, where the houses are still only the damaged ones. This should return an empty collection (since no colors are in the db yet).
irb(main):007:0> person.damaged_colors
Color Load (0.4ms)
SELECT "colors".* FROM "colors"
INNER JOIN "houses" ON "colors"."house_id" = "houses"."id"
WHERE "houses"."person_id" = ? AND "conditions"."condition" = 'damaged'
[["person_id", 1]]
SQLite3::SQLException: no such column: conditions.condition:
SELECT "colors".* FROM "colors"
INNER JOIN "houses" ON "colors"."house_id" = "houses"."id"
WHERE "houses"."person_id" = ? AND "conditions"."condition" = 'damaged'
It's clear from the sql string that the join table conditions
is missing and therefore conditions.condition
is not available. If I see it correctly, simply this string from the query before is missing:
INNER JOIN "conditions_houses" ON "conditions_houses"."house_id" = "houses"."id"
INNER JOIN "conditions" ON "conditions"."id" = "conditions_houses"."condition_id"
So the query should be:
SELECT "colors".* FROM "colors"
INNER JOIN "houses" ON "colors"."house_id" = "houses"."id"
INNER JOIN "conditions_houses" ON "conditions_houses"."house_id" = "houses"."id"
INNER JOIN "conditions" ON "conditions"."id" = "conditions_houses"."condition_id"
WHERE "houses"."person_id" = ? AND "conditions"."condition" = 'damaged'
Is this a rails bug or am I doing it wrong? Why is the join conditions
missing?
The code:
class Color < ActiveRecord::Base
belongs_to :house
end
class Condition < ActiveRecord::Base
has_and_belongs_to_many :houses
end
class House < ActiveRecord::Base
has_many :colors
belongs_to :person
has_and_belongs_to_many :conditions
scope :damaged, -> { joins(:conditions).where(:'conditions.condition' => 'damaged') }
end
class Person < ActiveRecord::Base
has_many :damaged_houses, -> { damaged }, :class_name => "House"
has_many :damaged_colors, through: :damaged_houses, :source => :colors
end