Ruby on rails 将对象数组转换为ActiveRecord::Relation

Ruby on rails 将对象数组转换为ActiveRecord::Relation,ruby-on-rails,ruby,activerecord,Ruby On Rails,Ruby,Activerecord,我有一个对象数组,让我们称之为指示器。我想在这个数组上运行指示符类方法(定义self.subjects的那些方法)。我知道在一组对象上运行类方法的唯一方法是让它们成为ActiveRecord::Relation。因此,我最终求助于将添加到数组的\u indicators方法 def to_indicators # TODO: Make this less terrible. Indicator.where id: self.pluck(:id) end 有时,我会在类方法中链接相当多的

我有一个对象数组,让我们称之为
指示器。我想在这个数组上运行指示符类方法(定义self.subjects的那些方法)。我知道在一组对象上运行类方法的唯一方法是让它们成为ActiveRecord::Relation。因此,我最终求助于将
添加到
数组
的\u indicators
方法

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
我想这可以归结为两个问题

  • 如何将对象数组转换为ActiveRecord::Relation?最好不要每次都执行
    ,其中
  • 在ActiveRecord::Relation上运行
    def self.subjects
    type方法时,如何访问ActiveRecord::Relation对象本身
  • 谢谢。如果我需要澄清什么,请告诉我

    如何将对象数组转换为ActiveRecord::Relation?最好不要每次都去哪里

    您无法将数组转换为ActiveRecord::关系,因为关系只是SQL查询的生成器,其方法不会对实际数据进行操作

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

    • 对于ActiveRecord 3.x,不要调用
      all
      ,而是调用,这将返回一个关系,该关系表示
      all
      将在数组中提供给您的相同记录

    • 对于ActiveRecord4.x,只需调用,它将返回一个关系

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


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

    您可以将对象数组
    arr
    转换为如下ActiveRecord::关系(假设您知道对象是哪个类,您可能会这样做)

    您必须使用
    where
    ,但这是一个非常有用的工具,您不应该不情愿地使用它。现在我们有了一个将数组转换为关系的单行程序

    map(&:id)
    将对象数组转换为仅包含其id的数组。将数组传递给where子句将生成一个SQL语句,该语句在
    中包含
    ,如下所示:

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

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

    在我的例子中,我需要将对象数组转换为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)
    

    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.to_sql
     => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"
    

    因此,不可能从独立于sql查询的对象数组中返回
    ActiveRecord::Relation

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

    这就是我的解决方案,我扩展了
    Array

    \lib/core\u ext/array.rb
    类数组
    def到_activerecord_的关系
    如果self.empty,是否返回ApplicationRecord.none?
    clazzes=self.collect(&:class).uniq
    如果clazzes.size>1,则raise“数组不能转换为ActiveRecord::Relation,因为它没有相同的元素”
    clazz=clazzes.first
    raise“元素类不是ApplicationRecord,因此无法转换”,除非clazz.祖先.include?应用记录
    分类位置(id:self.collect(&:id))
    结束
    结束
    
    一个使用示例是
    array.to\u activerecord\u relation.update\u all(状态:“finished”)
    。现在我在哪里使用它

    有时您需要过滤掉
    ActiveRecord::Relation
    ,例如,去掉未完成的元素。在这种情况下,最好使用scope
    元素。未完成
    ,您仍将保持
    ActiveRecord::Relation


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

    如果您尝试将该数组转换回关系的唯一原因是因为您通过
    获得它。所有
    ,只需使用
    。如Andrew Marshall的回答所示,使用
    (虽然在rails 4中,它可以与
    .all
    )一起使用。如果您发现自己需要将数组转换为关系,那么您在某个地方出了问题……做得好,这正是我所需要的。您可以将其用于模型上的任何属性。对我来说非常适合。既然您可以在
    where(id:arr.map(&:id))中执行操作,为什么还要自己构建文字SQL
    ?严格地说,这不会将数组转换为关系,而是会获得对象的新实例(一旦关系被转换)
    users.to_sql
     => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"