【问题标题】:Use Rails' select() to add (not overwrite) selected attributes?使用 Rails 的 select() 添加(而不是覆盖)选定的属性?
【发布时间】:2016-12-30 16:56:02
【问题描述】:

我有一个方便的范围,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 默认选择的现有列?

【问题讨论】:

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


    【解决方案1】:

    如果您深入研究 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 的内容(build_select 中的 if 分支),要么使用 table_name.*else 分支在build_select)。

    您应该能够使用build_select 使用的相同逻辑来确保select_values 在开始添加更多内容之前具有某些内容,以便您可以同时执行ifelsebuild_select 分支通过使用默认的table_name.* 预填充select_values。您可以将自己的select 版本修补到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
    

    然后说:

    > Post.select_append('6 as column_six').to_sql
     => "select `posts`.*, 6 as column_six from ..."
    

    同时保留“正常”select 行为:

    > Post.select('11 as column_eleven').to_sql
     => "select 11 as column_eleven from ..."
    

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

    【讨论】:

    • 我用过这个,但是在链接范围时它在 4.1 上不起作用。我还必须将delegate :select_append, to: :all 添加到ActiveRecord::Querying。此外,如果其他人想将其与 columns_on_demand gem 结合使用,您必须将 arel_table[Arel.tar] 替换为 default_select(true)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 2017-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多