36

我有模型资料。个人资料 has_one 用户。用户模型具有字段电子邮件。当我打电话

Profile.some_scope.includes(:user)

它调用

SELECT users.* FROM users WHERE users.id IN (some ids)

但是我的用户模型有很多我没有在渲染中使用的字段。是否可以仅加载来自用户的电子邮件?所以,SQL应该看起来像

SELECT users.email FROM users WHERE users.id IN (some ids)
4

6 回答 6

45

Rails 没有传递包含查询选项的功能。但是我们可以通过模型下的关联声明来传递这些参数。

对于您的场景,您需要在配置文件模型下创建与用户模型的新关联,如下所示

belongs_to :user_only_fetch_email, :select => "users.id, users.email", :class_name => "User"

我刚刚创建了一个关联,但它仅指向用户模型。所以你的查询将是,

Profile.includes(:user_only_fetch_email)

或者

Profile.includes(:user_only_fetch_email).find(some_profile_ids)
于 2012-09-13T15:23:26.143 回答
15

如果要选择特定属性,则应使用joins而不是includes.

从这个asciiccast

include 选项并不能真正与 select 选项一起使用,因为我们无法控制 SELECT 语句的第一部分是如何生成的。如果您需要控制 SELECT 中的字段,那么您应该使用连接而不是包含。

使用joins

Profile.some_scope.joins(:users).select("users.email")
于 2012-09-13T09:32:19.570 回答
11

你需要额外的属于模型。

对于简单的关联:

belongs_to :user_restricted, -> { select(:id, :email) }, class_name: 'User'

对于多态关联(例如,:commentable):

belongs_to :commentable_restricted, -> { select(:id, :title) }, polymorphic: true, foreign_type: :commentable_type, foreign_key: :commentable_id

你可以选择任何belongs_to你想要的名字。对于上面给出的示例,您可以像 等一样使用Article.featured.includes(:user_restricted)它们Comment.recent.includes(:commentable_restricted)

于 2014-12-09T01:38:46.963 回答
5

Rails 不支持在includes. 你知道,这只是lazy load

它使用ActiveRecord::Associations::Preloader模块在数据实际使用之前加载关联数据。通过方法

def preload(records, associations, preload_scope = nil)
    records = Array.wrap(records).compact

    if records.empty?
      []
    else
      records.uniq!
      Array.wrap(associations).flat_map { |association|
        preloaders_on association, records, preload_scope
      }
    end
 end

preload_scope, 的第三个参数preload是一种选择指定列的方法。但是不能再懒加载了

在 Rails 5.1.6

relation = Profile.where(id: [1,2,3])
user_columns = {:select=>[:updated_at, :id, :name]}
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(relation, :user, user_columns)

它将选择您传入的指定列。但是,它仅用于单个关联。您需要创建一个补丁ActiveRecord::Associations::Preloader来支持一次加载多个复杂的关联。

这是补丁的示例

使用方法,例子

于 2018-08-09T06:16:53.873 回答
2

我自己想要那个功能,所以请使用它。将此方法包含在您的课程中

#ACCEPTS 参数以字符串格式“ASSOCIATION_NAME:COLUMN_NAME-COLUMN_NAME”

def self.includes_with_select(*m)
    association_arr = []
    m.each do |part|
      parts = part.split(':')
      association = parts[0].to_sym
      select_columns = parts[1].split('-')
      association_macro = (self.reflect_on_association(association).macro)
      association_arr << association.to_sym
      class_name = self.reflect_on_association(association).class_name 
      self.send(association_macro, association, -> {select *select_columns}, class_name: "#{class_name.to_sym}")
    end
    self.includes(*association_arr)
  end

您将能够调用 like: Contract.includes_with_select('user:id-name-status', 'confirmation:confirmed-id'),它将选择那些指定的列。

于 2015-04-09T05:26:28.100 回答
0

使用 Mohanaj 的示例,您可以这样做:

belongs_to :user_only_fetch_email, -> { select [:id, :email] }, :class_name => "User"
于 2014-08-28T21:49:38.107 回答