如何急切加载与current_user的关联?

时间:2011-08-01 18:11:09

标签: ruby-on-rails ruby-on-rails-3 activerecord devise eager-loading

我在我的Rails应用程序中使用Devise进行身份验证。我想在我的一些控制器中加载一些用户相关模型。像这样:

class TeamsController < ApplicationController

  def show
    @team = Team.includes(:members).find params[:id]
    current_user.includes(:saved_listings)

    # normal controller stuff
  end
end

我怎样才能做到这一点?

4 个答案:

答案 0 :(得分:21)

我遇到了同样的问题,尽管每个人都说没有必要这样做,但我发现就像你一样。所以这对我有用:

# in application_controller.rb:
def current_user
  @current_user ||= super && User.includes(:saved_listings).find(@current_user.id)
end

请注意,这将加载所有控制器中的关联。对于我的用例,这正是我需要的。如果你真的只想在某些控制器中使用它,你将不得不再调整它。

这也会调用User.find两次,但是查询缓存不应该是一个问题,并且因为它可以防止大量额外的数据库命中,所以它仍然是性能提升。

答案 1 :(得分:9)

覆盖User模型中的serialize_from_session

class User
  devise :database_authenticatable

  def self.serialize_from_session key, salt
    record = where(id: key).eager_load(:saved_listings, roles: :accounts).first
    record if record && record.authenticatable_salt == salt
  end
end

然而,这将迫切加载所有请求。

答案 2 :(得分:1)

我想补充一下我认为是更好的解决方案。如评论中所述,现有解决方案可能会因find请求而两次进入您的数据库。相反,我们可以使用ActiveRecord::Associations::Preloader来利用Rails的工作来加载关联:

def current_user
  @current_user ||= super.tap do |user|
    ::ActiveRecord::Associations::Preloader.new.preload(user, :saved_listings)
  end
end

这将重新使用内存中的现有模型,而不是再次联接和查询整个表。

答案 3 :(得分:-3)

为什么不在模型上使用default_scope呢?

像这样:

Class User  < ActiveRecord::Base
  ...
  default_scope includes(:saved_listings)
  ...
end