使用Rails的select()添加(不覆盖)所选属性?

Und*_*ndo 9 activerecord ruby-on-rails rails-activerecord

我有一个方便的范围,includes相关的模型,以加快表格的渲染等:

class Post < ApplicationRecord
  ...

  scope :includes_for_post_row, -> { includes(:reasons).includes(:feedbacks => [:user]) }
Run Code Online (Sandbox Code Playgroud)

它工作正常.但是,现在,我想要select一个额外的属性.如果我已经知道我想要的初始属性,我可以这样做(在控制台中):

2.3.3 :005 > Post.select("`posts`.*, 42 AS column_forty_two").last.column_forty_two
  Post Load (1.0ms)  SELECT  `posts`.*, 42 AS column_forty_two FROM `posts` ORDER BY `posts`.`id` DESC LIMIT 1
 => 42 
Run Code Online (Sandbox Code Playgroud)

这假设我知道我想选择posts.*,然后我只是在我的column_forty_two专栏上,这一切都有效.

我想在column_forty_two不影响初始选择的情况下添加到我的结果中.例如,这应该工作:

p = Post.select("`posts`.*, 8 as column_eight").includes_for_post_row_with_forty_two
p.last.column_forty_two # => 42
p.last.column_eight # => 8
p.last.some_activerecord_property # => value
Run Code Online (Sandbox Code Playgroud)

应该这样:

p = Post.all.includes_for_post_row_with_forty_two.last
p.last.column_forty_two # => 42
p.last.some_activerecord_property # => value
Run Code Online (Sandbox Code Playgroud)

如何select在不影响或覆盖默认情况下或我之前选择的现有列的​​情况下添加其他列?.allselect

mu *_*ort 13

如果你去挖掘ActiveRecord源代码(通常是Rails的必要任务),你会看到发生了什么:

def build_select(arel)
  if select_values.any?
    arel.project(*arel_columns(select_values.uniq))
  else
    arel.project(@klass.arel_table[Arel.star])
  end
end
Run Code Online (Sandbox Code Playgroud)

select_values是您传递给的所有内容的列表,select默认情况下为空数组:

> Model.where(...).select_values
 => [] 
> Model.where(...).select('a').select_values
 => ["a"] 
> Model.where(...).select('a').select('b').select_values
 => ["a", "b"]
Run Code Online (Sandbox Code Playgroud)

当ActiveRecord最终解决构建SELECT子句时,它要么使用你传递给的select(if分支build_select)或者它使用table_name.*(else分支build_select).

在开始添加更多内容之前,您应该能够使用相同的逻辑build_select来确保select_values具有某些内容,以便通过预先填充默认值来执行ifelse分支.您可以将自己的版本修补到模块中:build_selectselect_valuestable_name.*selectActiveRecord::QueryMethods

module ActiveRecord
  module QueryMethods
    def select_append(*fields)
      if(!select_values.any?)
        fields.unshift(arel_table[Arel.star])
      end
      select(*fields)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

然后说出类似的话:

> Post.select_append('6 as column_six').to_sql
 => "select `posts`.*, 6 as column_six from ..."
Run Code Online (Sandbox Code Playgroud)

select单独留下"正常" 行为:

> Post.select('11 as column_eleven').to_sql
 => "select 11 as column_eleven from ..."
Run Code Online (Sandbox Code Playgroud)

你当然不需要修补补丁,但这种事情似乎是合理的.