【问题标题】:Prevent ActiveRecord Relation converting to Array防止 ActiveRecord 关系转换为数组
【发布时间】:2019-07-26 04:41:12
【问题描述】:

更新:

如何将虚拟属性添加到模型并保留活动记录关系。

我尝试了以下方法,但 .each 返回一个数组,而不是活动记录。我还可以使用什么其他方法?


我的set_listable_for 方法是将活动记录关系转换为数组。我想保留 ActiveRecord 关系。

在运行时,我向活动记录模型添加了 attr_access。

def add_listable_attribute_to(*relation)
  relation.each do |rel|
    rel[1].first.class.class_eval do
      attr_accessor :listable
    end
  end
end

然后我用这个方法将属性的值设置为所有记录的相同值....

   def set_listable_for(relation, object)
      relation.each do |record|
        record.listable = object
      end
    end

但是,我的 ActiveRecord 关系被转换为 Array afterwords。

如何保留 Active Record 关系,因为我不需要数组。由于我在这里继续使用它并继续对其进行范围和查询......

def union_scope(*relation)
  add_listable_attribute_to(*relation)

  listable = relation.first[0]
  combined = set_listable_for(relation.first[1], listable)
  relation.drop(1).each do |relation_set|
    listable = relation[0]
    set_listable_for(relation_set[1], listable)
    combined = combined.or(relation_set[1])
  end
  combined
end

谢谢

【问题讨论】:

  • 这正是ActiveRecord::Relation 的行为方式。它表示可以针对数据库运行的范围或查询(尚未包含记录)。一旦您需要一个或多个实际记录,它就会加载记录并将它们返回到一个数组中。如果您在关系上调用 firstcounteach 之类的方法,则会发生这种情况。因此,我不清楚您的问题,因为您要求禁用其核心功能。
  • @spickermann,我正在寻找一种替代解决方案,它可以替换我的代码,这样我就可以返回一个 AR 关系,而不是数组。

标签: ruby-on-rails ruby activerecord ruby-on-rails-5.2


【解决方案1】:

.each 调用执行查询并迭代结果。如果在作用域和分页之后在控制器中发生这种情况不会有问题,但是如果在作用域和分页之前调用它,则将加载整个模型数据集,这是不好的。

为避免这种情况,您需要在从数据库检索数据后尽可能晚地设置listable。我可以看到三种方法来处理这个问题:

  1. 在将关系加载到控制器或视图中后,使用装饰器包装关系实例。这更容易理解,但会将功能从模型层中提取出来。

  2. after_initialize 回调中设置listable。这保留了模型层中的功能,但增加了很多复杂性。

  3. 确保您只在确定范围后在控制器中调用 set_listable_for。 #1 的变体。

【讨论】:

  • 我希望可以,但是看到添加的代码,我仍在使用 AR Relation,所以需要另一个解决方案。
【解决方案2】:

通过在 Select 中添加一个“AS”语句,我能够返回一个 ActiveRecord 模型。唯一的问题是,当我调用 .count 时,我需要使用 .count(:all) 或 .count(:id) 来防止错误。

def union_scope(*relation)
  listable = relation.first[0]
  scope = relation.first[1]
  combined = scope.select("#{scope.table_name}.*, \'#{listable.class.name}\' as listable")
  relation.drop(1).each do |relation_set|
    listable = relation_set[0]
    scope = relation_set[1].select("#{scope.table_name}.*, \'#{listable.class.name}\' as listable")
    combined = combined.or(scope)
  end
  combined
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 2013-04-25
    • 1970-01-01
    • 2018-10-04
    • 2020-08-15
    相关资源
    最近更新 更多