我有一个对象数组,我们称之为Indicator
。我想在这个数组上运行Indicator类方法(那些def self.subjects
种类,范围等)。我知道在一组对象上运行类方法的唯一方法是让它们成为ActiveRecord :: Relation。因此,我最终要向to_indicators
添加Array
方法。
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
我想这可以归结为两个问题。
where
。def self.subjects
类型方法时,如何访问ActiveRecord :: Relation对象本身?感谢。如果我需要澄清任何事情,请告诉我。
答案 0 :(得分:149)
您可以将对象数组arr
转换为ActiveRecord :: Relation(假设您知道对象是哪个类,您可能会这样做)
MyModel.where(id: arr.map(&:id))
你必须使用where
,它是一个你不应该不愿意使用的有用工具。现在你有一个单行程序将数组转换为关系。
map(&:id)
会将您的对象数组转换为仅包含其ID的数组。将数组传递给where子句将生成一个IN
的SQL语句,其类似于:
SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....
请记住,数组的顺序将丢失 - 但由于您的目标只是在这些对象的集合上运行类方法,我假设这不会是个问题。
答案 1 :(得分:43)
如何将对象数组转换为ActiveRecord :: Relation?最好不要每次都做一次。
您无法将Array转换为ActiveRecord :: Relation,因为Relation只是SQL查询的构建器,其方法不能对实际数据进行操作。
但是,如果您想要的是关系,那么:
对于ActiveRecord 3.x,请不要致电all
而是调用scoped
,这会回复一个表示all
会给你的相同记录的关系在数组中。
对于ActiveRecord 4.x,只需调用all
即可返回一个关系。
在ActiveRecord :: Relation上运行
def self.subjects
类型方法时,如何访问ActiveRecord :: Relation对象本身?
在Relation对象上调用方法时,self
是关系(与其定义的模型类相对)。
答案 2 :(得分:4)
嗯,在我的情况下,我需要将一个对象数组转换为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)
答案 3 :(得分:0)
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"
因此不可能从独立于sql查询的对象数组中返回ActiveRecord::Relation
。
答案 4 :(得分:0)
首先,这不是灵丹妙药。根据我的经验,我发现转换为关系有时比其他方法容易。我尝试非常谨慎地使用这种方法,并且仅在替代方法更加复杂的情况下使用。
这是我的解决方案,我扩展了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周内生产并经过检查的未完成的元件。为了避免创建新的作用域,您可以过滤到一个数组,然后再转换回去。请记住,您仍然要对数据库进行查询,因为它是通过id
搜索的,但仍然是查询。