How do you fix the n + 1 query issue in ActiveRecord (Rails 3) when you also use 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 

Application that finds projects:

 @projects = Project.includes(:user_roles) 

So you can see, I say, that it includes the association of user roles in the request. However, I still see a problem with n + 1 queries: it finds roles once for each project.

If I remove the use of self.user_roles from the callback and look at the logs, I see that it finds projects and their user roles in two queries - one for projects and one for roles using project_id in (1,2,3,4,5...,n) .

Is there any way around this?

Let me explain a little bit: although I am ready to work on my specific situation, if necessary, I would prefer answers that focused on how to fix the problem as a whole. I can write kludge to get the data in the state that I want, without using the after_initialize callback and therefore without getting into the n + 1 query problem. However, I would prefer not to do this, so I prefer answers to a general problem, as opposed to my specific example.

+4
source share
3 answers

Even those who load are not available in after_initialize (they load after the record is initialized). See this issue with Rails for a discussion:

https://github.com/rails/rails/issues/13156

In connection with the original question: it looks like each project will have the same set of UserRole objects. I assume that has_many :through was disabled there, but even so, how does Project ever end without having a complete set? I don’t see how Project and UserRole are actually related here - from what is seen in the example:

 class Project < ActiveRecord::Base def user_roles UserRole.all end end 

will do the same as after_initialize ...

+1
source

Take a look at the rails http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

you can load a link when loading an object using includes

 User.find(2).includes(:assets)#will load all assets with user 

or you can specify in the model for an active load association

application / 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 upload comments for each post

+6
source

This is likely caused by the after_initialize callback, which runs every time each of the objects is initialized. If the callback is to automatically assign each role to each user (if it is not already assigned), you can do this using the before_save filter. Thus, the code will not work when performing a search for Project.includes(:user_roles) .

+1
source

All Articles