CollectionProxy 与 AssociationRelation

CollectionProxy vs AssociationRelation

我想知道 ActiveRecord::Associations::CollectionProxyActiveRecord::AssociationRelation 之间的区别。

class Vehicle < ActiveRecord::Base
  has_many :wheels
end

class Wheel < ActiveRecord::Base
  belongs_to :vehicle
end

所以如果我这样做:

v = Vehicle.new

v.wheels # => #<ActiveRecord::Associations::CollectionProxy []>

v.wheels.all # => #<ActiveRecord::AssociationRelation []>

我不知道它们之间有什么区别,为什么要这样实现?

好的。区别很简单。

根据您的示例进行解释:

v.wheels 中的关联代理有:

  • v中的对象作为@owner;
  • 它的轮子集合为@target;
  • @reflection 对象表示一个 :has_many 宏。

来自docs

Association proxies in Active Record are middlemen between the @owner and the @target. The @target object is not loaded until needed.

v = Vehicle.new
v.wheels # we are not sending any methods to @target object (collection of wheels)
# => #<ActiveRecord::Associations::CollectionProxy []>

这意味着,一旦您调用 @target 对象(在我们的例子中包含 wheels 的集合)上的任何方法,@target 就会被加载,并且它变为 ActiveRecord_AssociationRelation

v.wheels.all # sending the `all` method to @target (wheels)
# => #<ActiveRecord::AssociationRelation []>

ActiveRecord::Relation 是在转换为查询并执行之前的简单查询对象,另一方面,CollectionProxy 稍微复杂一些。

首先你得到关联扩展,你可能看到了类似这样的东西,假设一个有很多书的书店模型

class Store < ActiveRecord::Base
  has_many :books do
    def used
      where(is_used: true)
    end
  end
end

这样您就可以使用如下所示的语法调用商店中的旧书

Store.first.books.used

但这是最​​基本的用途,您可以使用集合代理中公开给您的属性,即ownerreflectiontarget

所有者

owner 提供对持有关联的父对象的引用

反思

reflection 对象是 ActiveRecord::Reflection::AssocciationReflection 的实例,包含关联的所有配置选项。

目标

target为关联集合对象(或has_onebelongs_to时为单个对象)。

使用这些方法,您可以在关联扩展中设置一些条件,例如,如果我们有一个博客,我们会向管理员用户授予对所有已删除帖子的访问权限(我知道这个蹩脚的例子)

Class Publisher < ActiveRecord::Base
  has_many :posts do
    def deleted
      if owner.admin?
        Post.where(deleted: true)
      else
        where(deleted: true)
      end
    end
  end
end

您还可以访问另外两个方法 resetreload,第一个 (reset) 清除缓存的关联对象,第二个 (reload) 更常见,用于 reset 然后从数据库中加载关联对象。

我希望这能解释 CollectionProxy class 为何如此有用