ActiveRecord包括.指定包含的列

iby*_*ich 31 ruby-on-rails ruby-on-rails-3 rails-activerecord

我有模特简介.个人资料has_one用户.用户模型有现场电子邮件.我打电话的时候

Profile.some_scope.includes(:user)
Run Code Online (Sandbox Code Playgroud)

它叫

SELECT users.* FROM users WHERE users.id IN (some ids)
Run Code Online (Sandbox Code Playgroud)

但我的用户模型有很多字段,我没有在渲染中使用.是否可以仅加载用户的电子邮件?所以,SQL应该是这样的

SELECT users.email FROM users WHERE users.id IN (some ids)
Run Code Online (Sandbox Code Playgroud)

小智 38

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

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

belongs_to :user_only_fetch_email, :select => "users.id, users.email", :class_name => "User"
Run Code Online (Sandbox Code Playgroud)

我只创建了一个关联,但它只指向User模型.所以你查询将是,

Profile.includes(:user_only_fetch_email)
Run Code Online (Sandbox Code Playgroud)

要么

Profile.includes(:user_only_fetch_email).find(some_profile_ids)
Run Code Online (Sandbox Code Playgroud)

  • 不得不改变语法以使其工作(Rails 4):`belongs_to:user_only_fetch_email, - > {select('users.id,users.email')},class_name:'User',foreign_key:'user_id' ` (8认同)
  • 不,它不起作用.但是感谢一个好主意. (3认同)
  • 使用select时必须添加foreign_key选项.从手册中:guides.rubyonrails.org/association_basics.html#belongs-to-association-reference 4.1.3.4 select select方法允许您覆盖用于检索有关关联对象的数据的SQL SELECT子句.默认情况下,Rails会检索所有列.如果在belongs_to关联上使用select方法,则还应设置:foreign_key选项以保证正确的结果. (2认同)

Chr*_*erg 13

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

从这个asciicast:

include选项实际上不适用于select选项,因为我们无法控制如何生成SELECT语句的第一部分.如果您需要控制SELECT中的字段,那么您应该使用include而不是include.

使用joins:

Profile.some_scope.joins(:users).select("users.email")
Run Code Online (Sandbox Code Playgroud)

  • 这是真的,但是:连接并没有完成对包含的关联的神奇渴望加载. (13认同)

Mus*_*ffa 8

您需要额外属于模型.

简单关联:

belongs_to :user_restricted, -> { select(:id, :email) }, class_name: 'User'
Run Code Online (Sandbox Code Playgroud)

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

belongs_to :commentable_restricted, -> { select(:id, :title) }, polymorphic: true, foreign_type: :commentable_type, foreign_key: :commentable_id
Run Code Online (Sandbox Code Playgroud)

您可以选择任何belongs_to您想要的名称.对于上面给出的例子,你可以使用他们喜欢的Article.featured.includes(:user_restricted),Comment.recent.includes(:commentable_restricted)等等.


Ear*_*hao 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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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

这是补丁的示例

使用方法,举例

  • 我喜欢这个解决方案,因为它可以防止在不相关的地方乱七八糟地使用belongs_to代码。请注意,在 Rails 6 中,`preload_scope` 更改为活动记录关系而不是哈希,因此在上面的示例中它将变为: `user_columns = User.select(:updated_at, :id, :name)` (2认同)