Ruby-on-rails 将对象数组转换为 ActiveRecord::Relation
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17331862/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Converting an array of objects to ActiveRecord::Relation
提问by Nathan
I have an array of objects, let's call it an Indicator. I want to run Indicator class methods (those of the def self.subjectsvariety, scopes, etc) on this array. The only way I know to run class methods on a group of objects is to have them be an ActiveRecord::Relation. So I end up resorting to adding a to_indicatorsmethod to Array.
我有一个对象数组,我们称之为Indicator. 我想def self.subjects在这个数组上运行 Indicator 类方法(各种、范围等)。我知道在一组对象上运行类方法的唯一方法是让它们成为 ActiveRecord::Relation。所以我最终求助于to_indicators向Array.
def to_indicators
# TODO: Make this less terrible.
Indicator.where id: self.pluck(:id)
end
At times I chain quite a few of these scopes to filter down the results, within the class methods. So, even though I call a method on an ActiveRecord::Relation, I don't know how to access that object. I can only get to the contents of it through all. But allis an Array. So then I have to convert that array to a ActiveRecord::Relation. For example, this is part of one of the methods:
有时我会在类方法中链接很多这样的作用域来过滤结果。因此,即使我在 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
I guess this condenses down to two questions.
我想这可以归结为两个问题。
- How can I convert an Array of objects to an ActiveRecord::Relation? Preferably without doing a
whereeach time. - When running a
def self.subjectstype method on an ActiveRecord::Relation, how do I access that ActiveRecord::Relation object itself?
- 如何将对象数组转换为 ActiveRecord::Relation?最好不要
where每次都做。 - 在
def self.subjectsActiveRecord::Relation 上运行类型方法时,如何访问该 ActiveRecord::Relation 对象本身?
Thanks. If I need to clarify anything, let me know.
谢谢。如果我需要澄清任何事情,请告诉我。
采纳答案by Andrew Marshall
How can I convert an Array of objects to an ActiveRecord::Relation? Preferably without doing a where each time.
如何将对象数组转换为 ActiveRecord::Relation?最好不要每次都做一个 where。
You cannot convert an Array to an ActiveRecord::Relation since a Relation is just a builder for a SQL query and its methods do not operate on actual data.
您不能将 Array 转换为 ActiveRecord::Relation,因为 Relation 只是 SQL 查询的构建器,其方法不对实际数据进行操作。
However, if what you want is a relation then:
但是,如果您想要的是关系,那么:
for ActiveRecord 3.x, don't call
alland instead callscoped, which will give back a Relation which represents the same records thatallwould give you in an Array.for ActiveRecord 4.x, simply call
all, which returns a Relation.
对于 ActiveRecord 3.x,不要调用
all而是调用scoped,这将返回一个 Relation ,该 Relation 表示all将在数组中提供相同的记录。对于 ActiveRecord 4.x,只需调用
all,它就会返回一个 Relation。
When running a
def self.subjectstype method on an ActiveRecord::Relation, how do I access that ActiveRecord::Relation object itself?
在
def self.subjectsActiveRecord::Relation 上运行类型方法时,如何访问该 ActiveRecord::Relation 对象本身?
When the method is called on a Relation object, selfis the relation (as opposed to the model class it's defined in).
当在 Relation 对象上调用该方法时,self是关系(与它在其中定义的模型类相反)。
回答by Marco Prins
You can convert an array of objects arrto an ActiveRecord::Relation like this (assuming you know which class the objects are, which you probably do)
您可以arr像这样将对象数组转换为 ActiveRecord::Relation(假设您知道对象是哪个类,您可能会这样做)
MyModel.where(id: arr.map(&:id))
You have to use wherethough, it's a useful tool which you shouldn't be reluctant to use. And now you have a one-liner converting an array to a relation.
但是您必须使用where它,它是一个有用的工具,您不应该不愿意使用。现在你有一个将数组转换为关系的单行程序。
map(&:id)will turn your array of objects to an array containing only their id's. And passing an array to a where clause will generate a SQL statement with INthat looks something like:
map(&:id)会将您的对象数组转换为仅包含其 id 的数组。并将数组传递给 where 子句将生成一个 SQL 语句,IN其内容类似于:
SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....
Keep in mind that the ordering of the array will be lost- But since your objective is only to run a class method on the collectionof these objects, I assume it won't be a problem.
请记住,数组的顺序将丢失- 但由于您的目标只是在这些对象的集合上运行类方法,我认为这不会有问题。
回答by Xingcheng Xia
Well, in my case, I need to converting an array of objects to ActiveRecord::Relationas well as sortingthem with a specific column(id for instance). Since I'm using MySQL, the field functioncould be helpful.
好吧,就我而言,我需要将对象数组转换为 ActiveRecord::Relation以及使用特定列(例如 id)对它们进行排序。由于我使用的是 MySQL,因此field 函数可能会有所帮助。
MyModel.where('id in (?)',ids).order("field(id,#{ids.join(",")})")
The SQL looks like:
SQL 看起来像:
SELECT ... FROM ... WHERE (id in (11,5,6,7,8,9,10))
ORDER BY field(id,11,5,6,7,8,9,10)
回答by ray
ActiveRecord::Relationbinds database query which retrieves data from database.
ActiveRecord::Relation绑定从数据库中检索数据的数据库查询。
Suppose to make sense, We have array with objects of same class, then with which query we suppose to bind them?
假设有意义,我们有具有相同类对象的数组,那么我们假设用哪个查询绑定它们?
When I run,
当我跑步时,
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
Here in above, usersreturn Relationobject but binds database query behind it and you can view it,
在上面,users返回Relation对象但在其后面绑定数据库查询,您可以查看它,
users.to_sql
=> "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5) ORDER BY created_at desc"
So it is not possible to return ActiveRecord::Relationfrom array of objects which is independent of sql query.
所以不可能ActiveRecord::Relation从独立于 sql 查询的对象数组中返回。
回答by Haris Krajina
First of all, this is NOT a silver bullet. Out of my experience, I found that converting to relation is sometimes easier than alternatives. I try to use this approach very sparingly and only in cases where the alternative would be more complex.
首先,这不是灵丹妙药。根据我的经验,我发现转换为关系有时比替代方案更容易。我尝试非常谨慎地使用这种方法,并且仅在替代方案更复杂的情况下使用。
That being said here is my solution, I've extended Arrayclass
这就是我的解决方案,我已经扩展了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
A usage example would be array.to_activerecord_relation.update_all(status: 'finished'). Now where do I use it?
一个使用示例是array.to_activerecord_relation.update_all(status: 'finished'). 现在我在哪里使用它?
Sometimes you need to filter out ActiveRecord::Relationfor example take out not completed elements. In those cases best is to use scope elements.not_finishedand you would still keep ActiveRecord::Relation.
有时您需要过滤掉ActiveRecord::Relation例如取出未完成的元素。在这些情况下,最好使用 scopeelements.not_finished并且您仍然会保留ActiveRecord::Relation.
But sometimes that condition is more complex. Take out all elements that are not finished, and that has been produced in the last 4 weeks and have been inspected. To avoid creating new scopes you can filter to an array and then convert back. Keep in mind that you still do a query to DB, quick since it searches by idbut still a query.
但有时这种情况更复杂。取出所有未完成的元件,以及过去 4 周内已生产并经过检查的元件。为避免创建新范围,您可以过滤到一个数组,然后再转换回来。请记住,您仍然对数据库进行查询,速度很快,因为它搜索id但仍然是查询。

