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

时间:2016-12-30 16:56:03

标签: ruby-on-rails activerecord rails-activerecord

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

class Post < ApplicationRecord
  ...

  scope :includes_for_post_row, -> { includes(:reasons).includes(:feedbacks => [:user]) }

工作正常。但是,现在,我想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 

这假设我知道我想要选择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

应该这样:

p = Post.all.includes_for_post_row_with_forty_two.last
p.last.column_forty_two # => 42
p.last.some_activerecord_property # => value

我如何select 其他列,而不会影响或覆盖.all或我之前select默认选择的现有列?

1 个答案:

答案 0 :(得分:9)

如果你要深入研究ActiveRecord源代码(Rails经常是必要的任务),you'll see what's going on

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

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

> Model.where(...).select_values
 => [] 
> Model.where(...).select('a').select_values
 => ["a"] 
> Model.where(...).select('a').select('b').select_values
 => ["a", "b"]

当ActiveRecord最终解决构建SELECT子句时,它会使用您传递给select的内容(if中的build_select分支)或者它使用{ {1}} table_name.*中的else分支。

在开始添加更多内容之前,您应该能够使用build_select使用的相同逻辑来确保build_select具有某些内容,以便您同时执行select_values和{{通过使用默认if预填else来{1}}的分支。您可以将自己的build_select版本修补到select_values模块中:

table_name.*

然后说出类似的话:

select

离开&#34;正常&#34>仅ActiveRecord::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

你当然不必修补补丁,但这种事情似乎是合理的。