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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:14:22  来源:igfitidea点击:

Converting an array of objects to ActiveRecord::Relation

ruby-on-railsrubyactiverecord

提问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_indicatorsArray.

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.

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

  1. How can I convert an Array of objects to an ActiveRecord::Relation? Preferably without doing a whereeach time.
  2. When running a def self.subjectstype method on an ActiveRecord::Relation, how do I access that ActiveRecord::Relation object itself?
  1. 如何将对象数组转换为 ActiveRecord::Relation?最好不要where每次都做。
  2. 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 call scoped, which will give back a Relation which represents the same records that allwould 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)

MySQL field function

MySQL 字段函数

回答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但仍然是查询。