有条件地将模型附加到收集

时间:2013-09-12 01:02:08

标签: ruby-on-rails collections ruby-on-rails-4

我正试图有条件地建立一个模型列表。即。

@items = []

if some_condition
    @items << MyModel.where(...)
end

if another_condition
    @items << MyModel.where(...)
end

...

这不起作用。它实际上会构建一个正确对象的数组,但是当我访问数组中项目的字段和关系时,它们“找不到”。我尝试了其他一些内容,例如@items = MyModel.none@items = {}以及.merge,但都没有效果。我似乎无法弄清楚这一点。

有条件地建立像这样的集合的最佳方法是什么?

更新我希望能够维护Relation,以便我可以继续使用.where.first和其余的Relation来查询它{{1}}方法。

3 个答案:

答案 0 :(得分:5)

<<会将一个项目附加到数组中,因此查询将不会被运行,而是作为ActiveRecord::Relation附加,如果您使用all,则最终会数组数组。您应该使用concat附加整个集合(+=也可以工作,但如果您的查询返回大量记录,则会实例化影响性能的不必要的临时数组):

@items = []

if some_condition
    @items.concat(MyModel.where(...))
end

if another_condition
    @items.concat(MyModel.where(...))
end

答案 1 :(得分:3)

您的串联方法将导致多个数据库查询,并且不会是可链接的,这通常是分页,范围,分组和排序所必需的。

我会收集您的条件并在最后结合。看起来您的条件实际上是OR类型查询,而不是更容易链接的AND类型查询。

请执行以下操作:

@queries = []

if some_condition
  # Relation condition
  @queries << MyModel.where(...)
end

if another_condition
  # another Relation condition
  @queries << MyModel.where(...)
end

if and_another_condition
  # Hash equality and IN conditions
  @queries << { attr1: 'foo', attr2: [1,2,3] }
end

if yet_another_condition
  # string condition with argument
  @queries << ['attr LIKE ? ', arg]
end

@items = MyModel.any_of(*queries).order(...).page(...).per(...)

神奇的是一个漂亮的自定义AR扩展方法any_of?,用于使用Arel组合OR类型查询。它可以使用Relations,String条件,Hash条件或Arrays来splatting到where()子句。

# put in config/initializers/ar_any_of.rb or in lib/xxxx
class ActiveRecord::Base
  def self.any_of(*queries)
    where(
      queries.map { |query|
        query = where(query) if [String, Hash].any? { |type| query.kind_of? type }
        query = where(*query) if query.kind_of? Array
        query.arel.constraints.reduce(:and)
      }.reduce(:or)
    )
  end
end

可以使用以下各种条件生成单个SQL:

Country.any_of(
  Country.where(alpha2: 'AU'),
  { alpha2: ['NZ', 'UK'] },
  ['alpha2 LIKE ?', 'U%']).to_sql

# => "SELECT \"countries\".* FROM \"countries\"  WHERE (((\"countries\".\"alpha2\" = 'AU' OR \"countries\".\"alpha2\" IN ('NZ', 'AD')) OR (alpha2 LIKE 'U%')))"

答案 2 :(得分:-1)

我认为答案很简单。

您可以将收藏集初始化为anonymous scope

@items = MyModel.scoped

这是一个ActiveRecord :: Relation。

**请注意,{4}已弃用scopedall执行相同的操作http://blog.remarkablelabs.com/2012/12/what-s-new-in-active-record-rails-4-countdown-to-2013,因此我们的示例将是

@items = MyModel.all

在此之后,链接额外条件(按条件)应该非常简单:

@items = @items.where(owner: me) if me.present?

@items = @items.group(:attribute_1) if show_groups