Rails包含未在模型方法

时间:2016-07-01 17:38:53

标签: ruby-on-rails optimization rails-api

因此,在我正在开发的rails-api中,我们目前正在尝试优化一些较长时间运行的呼叫,并且我遇到了.includes功能的问题。我在大多数情况下都能使用它,但有一种特殊的情况是它没有以我想要的方式工作。

以下是一个例子:

用户类

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方法中调用它时,它不再进行db调用,但这不是案子。我在这里做错了什么?

谢谢, 查理

2 个答案:

答案 0 :(得分:1)

你几乎就在那里! 试试这种方法:

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 类和 FacebookAuth 类中加载图像数据。

我修复了你的方法 User#get_profile_image 中的其他一些问题:

  • 总是返回零。我相信你的真实代码会提前退货。
  • 对于集合,如果查找facebook_auth_tokens,它会执行N + 1查询。

答案 1 :(得分:0)

嗯,我想发表评论,但是无法将代码放入评论中,所以我给出了一个不回答...

我没有看到任何明显错误,但作为一种解决方法,你可以在用户或某个地方做到这一点:

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

只需调用/缓存控制器中的图像,如果这不是过于简单......

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

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