在使用after_initialize回调时,如何修复ActiveRecord(Rails 3)中的n + 1查询问题?

Sam*_*rbi 5 ruby activerecord ruby-on-rails ruby-on-rails-3

模型:

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
Run Code Online (Sandbox Code Playgroud)

查找项目的声明:

@projects = Project.includes(:user_roles)
Run Code Online (Sandbox Code Playgroud)

所以你可以看到,我告诉它在查询中包含用户角色关联.但是,我仍然看到n + 1查询问题:它为每个项目找到一次角色.

如果我self.user_roles从回调中删除使用并查看日志,我可以看到它在2个查询中找到项目及其用户角色 - 一个用于项目,一个用于角色使用project_id in (1,2,3,4,5...,n).

有办法解决这个问题吗?

让我澄清一点:虽然我愿意在需要时解决我的具体情况,但我更喜欢那些专注于如何解决问题的答案.我能够编写一个kludge来使数据处于我想要的状态而不使用after_initialize回调,因此不会进入n + 1查询问题.但是,我宁愿不这样做,所以我更喜欢一般问题的答案而不是我的具体例子.

Nav*_*eed 6

看看rails eager loading http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

您可以在使用加载对象时加载关联 includes

User.find(2).includes(:assets)#will load all assets with user
Run Code Online (Sandbox Code Playgroud)

或者您可以在模型中指定急切加载关联

应用程序/模型/ user.rb

class User< AR::Base
   has_many :posts,:include=>:comments
end

class Post < AR::Base
  has_many :comments
  belongs_to :user
end
Run Code Online (Sandbox Code Playgroud)

现在u.posts将为每个帖子加载评论


小智 2

即使是急切加载的关联也不可用(它们在记录初始化after_initialize加载)。请参阅这个 Rails 问题进行一些讨论:

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

与最初的问题相关:看起来每个项目都会有相同的UserRole对象集。我猜有一个has_many :through已经被清理掉了,但即便如此,怎么会Project最终没有全套呢?我没有看到ProjectUserRole在这里实际上是如何连接的 - 从示例中可见,这是:

class Project < ActiveRecord::Base
  def user_roles
    UserRole.all
  end
end
Run Code Online (Sandbox Code Playgroud)

after_initialize会完成与...相同的事情