为什么 Rails 模型关联结果不是自然的 ActiveRecord::Relations



我正在使用Rails 3.2.0

假设我有:

class Comment < ActiveRecord::Base
  has_many :articles
end
c1 = Comment.last

然后

c1.articles.class
# => Array
c1.articles.where('id NOT IN (999999)').class
# => ActiveRecord::Relation    

为什么关联的结果是一种ActiveRecord::Relation

它显然是/曾经在某个时候

c1.articles.to_orig
# undefined method `to_orig' for #<ActiveRecord::Relation:0x007fd820cc80a8>
c1.articles.class
# => Array

某些计算作用于 ActiveRecord::Relation 对象,但检查类会给出不同的类型。


特别是,这会在使用merge连接多个查询时中断构建延迟加载的查询。

这是一个

ActiveRecord::Relation,但Rails故意骗你。您可以在方法调用中看到这一点,并通过调用 ancestors 继续看到它,其中包括大量 ActiveRecord 类:

c1.articles.ancestors.select { |c| c.to_s =~ /ActiveRecord/ }.size  #=> 35

这表明它在很大程度上是一个Array.

发生这种情况是因为您在调用c1.articles时得到的是一个ActiveRecord::Associations::CollectionProxy *,它取消了class的定义(以及许多其他方法(。这意味着class通过其method_missing被委托,将其发送到target。正如我们所看到的,这里的target类实际上是Array

c1.articles.target.class  #=> Array

这就是c1.articles.class的来源。尽管如此,这是一个ActiveRecord::Relation.

* 我们可以通过在相关对象上调用 Ruby 的原始 class 方法来验证它确实是一个ActiveRecord::Associations::CollectionProxyObject.instance_method(:class).bind(c1.articles).call 。这是一个很好的技巧,可以验证对象是否没有尝试伪装成不同的类。

因为当您定义关联时,它会在您的模型中放置:

def #{name}(*args)
  association(:#{name}).reader(*args)
end

.reader(( 返回 AssociationProxy,它将删除 .class 方法并将未知方法委托给通过.method_missing@target

相关内容

  • 没有找到相关文章

最新更新