Rails 包括未在模型方法中使用的缓存

Rails includes cache not being used in model method

因此,在我正在处理的 rails-api 中,我们目前正在尝试优化一些较长的 运行 调用,但我遇到了问题使用 .includes 功能。我在大多数情况下都能正常工作,但在一种特殊情况下它无法按照我想要的方式工作。

这是一个例子:

用户class

class User < ActiveRecord::Base
  has_many :images
  has_one :active_image, -> { where(images: { active_image: true })}, class_name: 'Image'
  has_many :facebook_auth

  def get_profile_image
    if active_image
      active_image.image.url(:profile)
    else
      facebook = facebook_auth.last
      if facebook
        "https://graph.facebook.com/#{facebook.provider_user_id}/picture?width=150&height=150"
      end
    end
    nil
  end
end

控制器:

class UserController < BaseAPIController
  def get_user_image
    user_id = params[:user_id]
    user = User.includes(:active_image, :facebook_auth).find(user_id)
    render json: user.get_profile_image
  end
end

有了这个,我假设 .includes(:active_image, :facebook_auth) 会缓存数据,这样当我在 get_profile_image 方法中调用它们时,它不会再进行任何数据库调用,但这是是这样的。我在这里做错了什么?

谢谢, 查理

好吧,我想发表评论,但无法将代码放入评论中,所以我给出了一个非答案...

我没有发现任何明显的错误,但作为解决方法,您可以在 User 或其他地方执行此操作:

def self.user_profile_image(user_id)
  active_image = Images.where(user_id: user_id).where(active_image: true).first

  if active_image
      active_image.image.url(:profile)
  else
    facebook = FaceBookAuth.where(user_id: user_id).last
    if facebook
      "https://graph.facebook.com/#{facebook.provider_user_id}/picture?width=150&height=150"
    end
  end

  nil
end

只要 call/cache 控制器中的图像,如果这不是过于简单的话...

def get_user_image
  render json: User.user_profile_image(params[:user_id])
end

这最多使 2 个相对有效的查询成为可能。它不会不必要地加载用户等

你快到了! 试试这个方法:

class User < ApplicationRecord
  has_many :images, dependent: :destroy
  has_one :active_image,
    -> { where(active: true) },
    class_name: 'Image'

  has_many :facebook_auths, dependent: :destroy
  has_one :active_facebook_auth,
    -> { order("created_at desc") },
    class_name: 'FacebookAuth'

  scope :eager_load_image_data,
    -> { includes(:active_image).includes(:active_facebook_auth) }

  def profile_image_url
    if active_image
      active_image.url
    elsif active_facebook_auth
      "https://graph.facebook.com/#{active_facebook_auth.provider_user_id}/picture?width=150&height=150"
    else
      nil
    end
  end
end

然后在您的控制器中或任何您想要加载图像的时候:

# for one user, with id 2:
User.eager_load_image_data.find(2).profile_image_url

# for a collection (using 'all' here):
User.eager_load_image_data.all.map{ |user|
  [user.name, user.profile_image_url] 
}

通过这种方式,可以从 Image class 和 FacebookAuth class 中预先加载图像数据.

你的方法 User#get_profile_image 中还有一些其他问题,我已修复:

  • 它总是 returns 零。我确信在你的真实代码中你早 returns.
  • 对于集合,如果要查找 facebook_auth_tokens,它会进行 N+1 查询。