# Models
class Animal < ActiveRecord::Base
belongs_to :zoo
end
class Dog < Animal
belongs_to :leg
end
class Snake < Animal
end
class Leg < ActiveRecord::Base
end
class Zoo
has_many :animals
end
# Test, which fails with
# Association named 'leg' was not found on Animal; perhaps you misspelled it?
Zoo.first.animals.
.where(:type => 'Dog')
.includes(:leg)
In this example, Rails can not know the specific type of the objects queried (it would have to analyze the where
statement for that, which it does not appear to do). Therefore, it fails, as the association is not defined on the generic model Animal
, but on the Dog
model.
Is there a way of specifying the type of the objects about to be retrieved, so that the example works?
Here's a solution that looks inefficient at first, but is actually very performant due to the way ActiveRecord builds up its query object.
A good solution to your query is:
Dog.where(id: Zoo.first.animals.where(type: 'Dog').select(:id))
.includes(:leg)
At first glance, it looks like it should execute 2 queries, but ActiveRecord is smart enough to produce the following SQL in a single DB call:
SELECT "animals".*
FROM "animals"
WHERE "animals"."id" IN (
SELECT "animals"."id"
FROM "animals"
WHERE "animals"."id" = 111
AND "animals"."type" = 'Dog'
)
Since the outer query is driven by the Dog
class, then the .includes
will work. And so this is a generic way of behaving like the becomes method on an ActiveRecord::Relation.
Note: The .select(:id)
part of the code isn't actually required. I just included that for clarity, to show how AR uses that part of the query.