111

我有一个对象数组,我们称它为Indicator. 我想def self.subjects在这个数组上运行指标类方法(种类、范围等)。我知道在一组对象上运行类方法的唯一方法是让它们成为 ActiveRecord::Relation。所以我最终求助于to_indicatorsArray.

def to_indicators
  # TODO: Make this less terrible.
  Indicator.where id: self.pluck(:id)
end

有时我会在类方法中链接很多这些范围来过滤结果。因此,即使我在 ActiveRecord::Relation 上调用方法,我也不知道如何访问该对象。我只能通过all. 但是all是一个数组。那么我必须将该数组转换为 ActiveRecord::Relation。例如,这是其中一种方法的一部分:

all.to_indicators.applicable_for_bank(id).each do |indicator|
  total += indicator.residual_risk_for(id)
  indicator_count += 1 if indicator.completed_by?(id)
end

我想这可以归结为两个问题。

  1. 如何将对象数组转换为 ActiveRecord::Relation?最好不要where每次都做。
  2. 在 ActiveRecord::Relation 上运行def self.subjects类型方法时,如何访问该 ActiveRecord::Relation 对象本身?

谢谢。如果我需要澄清任何事情,请告诉我。

4

5 回答 5

176

您可以像这样将对象数组转换为arrActiveRecord::Relation(假设您知道对象是哪个类,您可能会这样做)

MyModel.where(id: arr.map(&:id))

你必须使用where它,它是一个有用的工具,你不应该不愿意使用。现在你有一个将数组转换为关系的单行代码。

map(&:id)会将您的对象数组转换为仅包含其 id 的数组。将数组传递给 where 子句将生成一条 SQL 语句,IN其内容类似于:

SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....

请记住,数组的顺序将会丢失- 但是由于您的目标只是在这些对象的集合上运行类方法,我认为这不会成为问题。

于 2014-06-27T09:24:23.910 回答
47

如何将对象数组转换为 ActiveRecord::Relation?最好每次都不做 where 。

您不能将 Array 转换为 ActiveRecord::Relation,因为 Relation 只是 SQL 查询的构建器,它的方法不对实际数据进行操作。

但是,如果您想要的是一种关系,那么:

  • 对于 ActiveRecord 3.x,不要调用all,而是调用scoped,这将返回一个 Relation,它表示与all数组相同的记录。

  • 对于 ActiveRecord 4.x,只需调用all,它会返回一个关系。

在 ActiveRecord::Relation 上运行def self.subjects类型方法时,如何访问该 ActiveRecord::Relation 对象本身?

当在 Relation 对象上调用该方法时,self是关系(与定义它的模型类相反)。

于 2013-06-26T23:29:11.753 回答
7

好吧,就我而言,我需要将对象数组转换为 ActiveRecord::Relation并使用特定列(例如 id)对它们进行排序。由于我使用的是 MySQL,因此字段函数可能会有所帮助。

MyModel.where('id in (?)',ids).order("field(id,#{ids.join(",")})") 

SQL 看起来像:

SELECT ... FROM ... WHERE (id in (11,5,6,7,8,9,10))  
ORDER BY field(id,11,5,6,7,8,9,10)

MySQL 字段函数

于 2017-05-04T09:06:27.957 回答
1

ActiveRecord::Relation绑定从数据库中检索数据的数据库查询。

假设有意义,我们有一个包含相同类对象的数组,那么我们应该使用哪个查询来绑定它们?

当我跑步时,

users = User.where(id: [1,3,4,5])
  User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc

在上面,users返回Relation对象但在其后面绑定数据库查询,您可以查看它,

users.to_sql
 => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"

所以不可能ActiveRecord::Relation从独立于 sql 查询的对象数组中返回。

于 2019-01-28T13:04:30.260 回答
1

首先,这不是灵丹妙药。根据我的经验,我发现转换为关系有时比其他方法更容易。我尝试非常谨慎地使用这种方法,并且仅在替代方案更复杂的情况下使用。

这就是我的解决方案,我已经扩展了Array课程

# lib/core_ext/array.rb

class Array

  def to_activerecord_relation
    return ApplicationRecord.none if self.empty?

    clazzes = self.collect(&:class).uniq
    raise 'Array cannot be converted to ActiveRecord::Relation since it does not have same elements' if clazzes.size > 1

    clazz = clazzes.first
    raise 'Element class is not ApplicationRecord and as such cannot be converted' unless clazz.ancestors.include? ApplicationRecord

    clazz.where(id: self.collect(&:id))
  end
end

一个使用示例是array.to_activerecord_relation.update_all(status: 'finished'). 现在我在哪里使用它?

有时您需要过滤掉ActiveRecord::Relation例如取出未完成的元素。在这些情况下,最好是使用范围elements.not_finished,你仍然会保留ActiveRecord::Relation.

但有时这种情况更为复杂。取出所有未完成的元素,以及在过去 4 周内生产并经过检查的元素。为避免创建新范围,您可以过滤到数组,然后再转换回来。请记住,您仍然可以快速查询 DB,因为它搜索id但仍然是查询。

于 2019-04-10T12:52:43.837 回答