12

我正在使用 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特别是,当用于连接多个查询时,这会破坏构建延迟加载的查询。

4

2 回答 2

16

它是一个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_missingtargettargetArray

c1.articles.target.class  #=> Array

那是c1.articles.class从哪里来的。尽管如此,它一个ActiveRecord::Relation.

*我们可以通过在有问题的对象上调用 Ruby 的原始方法来验证它确实是一个: . 这是一个很好的技巧,可以验证对象没有试图假装属于不同的类。ActiveRecord::Associations::CollectionProxyclassObject.instance_method(:class).bind(c1.articles).call

于 2012-12-28T18:10:35.867 回答
2

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

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

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

于 2012-12-28T17:54:15.523 回答