When you eagerly load an association list using the .include
option, and at the same time have a .where
on an included table, two things happen:
where
condition, even though you only wanted to use the where
condition to filter the containing model.The second case's behavior is mostly unexpected, because pre-loaded associations usually don't care about the circumstances under which their containing model was found.
Take this class:
class Activity
has_many :users
end
Here activity 42 has four users:
activity = Activity.find(42)
activity.users.ids
# => [1, 2, 3, 4]
Let's say we want to do the same on all activities that belong to a user. To this, we scope on Activity
.
Note how the activity from above now suddenly no longer contains any other users:
activity = Activity.
.includes(:users)
.where(users: { id: [4] })
.find(42)
activity.users.ids # here happens the unexpected
# => [4]
By reloading the object, its full list of associated users is restored:
activity.reload.users.ids
# => [1, 2, 3, 4]
Or you can reset the association cache:
activity.users.reset # newer Rails
activity.users(true) # old Rails
In newer Rails versions you should prefer to use joins
and then preload
if necessary (which will trigger a second query):
# Join for the condition, but do not preload
activities = Activity.joins(:users).where(users: { id: [4] })
# Preload associations with a second query on users;
# Does not make a join.
activities = activities.preload(:users)