How do you fix the n+1 queries problem in ActiveRecord (Rails 3) when also using the after_initialize callback?
Model:
class Project < ActiveRecord::Base
has_many :user_roles
after_initialize :add_user_roles
def add_user_roles
UserRoles.all.each do |ur|
self.user_roles << ur unless self.user_roles.include?(ur)
end
end
end
Statement that finds the projects:
@projects = Project.includes(:user_roles)
So you can see, I'm telling it to include the user roles association in the query. However, I'm still seeing the n+1 queries problem: it finds the roles once for each project.
If I remove the usage of self.user_roles
from the callback and look at the logs, I can see it finds the projects and their user roles in 2 queries - one for the projects, and one for the roles using project_id in (1,2,3,4,5...,n)
.
Is there a way to work around this?
Let me clarify a bit: While I'm willing to work around my specific situation if req开发者_如何学Pythonuired, I'd much prefer answers that focused on how to fix the problem in general. I am capable of writing a kludge to get the data in the state that I want it without using the after_initialize callback, and hence not coming into the n+1 queries problem. However, I would rather not do that, so I prefer answers to the general problem as opposed to my specific example.
take a look at rails eager loading http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
you can load association while loading object using includes
User.find(2).includes(:assets)#will load all assets with user
or you can specify in model to eager load association
app/models/user.rb
class User< AR::Base
has_many :posts,:include=>:comments
end
class Post < AR::Base
has_many :comments
belongs_to :user
end
now u.posts
will load comments for each post
Even eager-loaded associations aren't available in after_initialize
(they get loaded after the record is initialized). See this Rails issue for some discussion:
https://github.com/rails/rails/issues/13156
Related to the original question: it looks like every project will have the same set of UserRole
objects. I'm guessing there's a has_many :through
that's been sanitized out, but even so, how does a Project
ever wind up not having the full set? I'm not seeing how Project
and UserRole
are actually connected here - from what's visible in the example, this:
class Project < ActiveRecord::Base
def user_roles
UserRole.all
end
end
would accomplish the same thing as the after_initialize
...
This is likely caused by the after_initialize callback, which is run every time each of the objects are initialized. If the point of the callback is to automatically assign every role to every user (unless already assigned), then you could do this via a before_save
filter instead. That way, the code won't run when doing your Project.includes(:user_roles)
finder.
精彩评论