Ruby-on-rails ActiveRecord 包括。指定包含的列

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12400860/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 20:44:58  来源:igfitidea点击:

ActiveRecord includes. Specify included columns

ruby-on-railsruby-on-rails-3rails-activerecord

提问by ibylich

I have model Profile. Profile has_one User. User model has field email. When I call

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

Profile.some_scope.includes(:user)

it calls

它叫

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

But my User model has many fields that I am not using in rendering. Is it possible to load only emails from users? So, SQL should look like

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

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

回答by Mohanraj

Rails dont have the facility to pass the options for includequery. But we can pass these params with the association declarationunder the model.

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

For your scenario, you need to create a new association with users model under the profile model, like below

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

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

I just created one more association but it points to User model only. So your query will be,

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

Profile.includes(:user_only_fetch_email)

or

或者

Profile.includes(:user_only_fetch_email).find(some_profile_ids)

回答by Chris Salzberg

If you want to select specific attributes, you should use joinsrather than includes.

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

From this asciicast:

从这个asciicast

the include option doesn't really work with the select option as we don't have control over how the first part of the SELECT statement is generated. If you need control over the fields in the SELECT then you should use joins over include.

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

Using joins:

使用joins

Profile.some_scope.joins(:users).select("users.email")

回答by Musaffa

You need extra belongs to in the model.

您需要额外的属于模型。

For simple association:

对于简单关联:

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

For Polymorphic association (for example, :commentable):

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

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

You can choose whatever belongs_toname you want. For the examples given above, you can use them like Article.featured.includes(:user_restricted), Comment.recent.includes(:commentable_restricted)etc.

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

回答by EarlyZhao

Rails does not support to select specific columns when includes. You know ,it's just lazy load.

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

It use the ActiveRecord::Associations::Preloadermodule to load the associated data before data actually using. By the method:

它使用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_scopethe third params of preload, is a way to select specify columns. But can't lazy load anymore.

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

At Rails 5.1.6

在 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)

It will select the specify columns you passed in. But, it just for single association. You need create a patch for ActiveRecord::Associations::Preloaderto support loading multiple complex associations at once.

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

Here is a example for patch

这是补丁的示例

The way to use it, example

使用方法,举例

回答by ClassyPimp

I wanted that functionality myself,so please use it. Include this method in your class

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

#ACCEPTS args in string format "ASSOCIATION_NAME:COLUMN_NAME-COLUMN_NAME"

#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

And you will be able to call like: Contract.includes_with_select('user:id-name-status', 'confirmation:confirmed-id'), and it will select those specified columns.

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

回答by Yan Zhao

Using Mohanaj's example, you can do this:

使用 Mohanaj 的示例,您可以执行以下操作:

belongs_to :user_only_fetch_email, -> { select [:id, :email] }, :class_name => "User"