【问题标题】:Why are Rails model association results not naturally ActiveRecord::Relations?为什么 Rails 模型关联结果自然不是 ActiveRecord::Relations?
【发布时间】:2013-12-10 22:55:05
【问题描述】:

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

【问题讨论】:

  • 如果我没记错的话,类方法是在骗你——它委托给目标,这是一个数组

标签: ruby-on-rails activerecord types lazy-loading


【解决方案1】:

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

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

这表明它非常不是Array

这是因为调用c1.articles 时返回的是ActiveRecord::Associations::CollectionProxy*,undefines class(以及许多其他方法)。这意味着class 是通过its method_missing 委托的,sends it to 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。这是一个很好的技巧,可以验证对象没有试图假装属于不同的类。

【讨论】:

  • @AndrewMarshall - 我很想听听您对这种情况的看法:stackoverflow.com/questions/16927437/…
  • 在与 Array 服装中的 CollectionProxy 战斗了一整周后,找到这个问题/答案让我恢复了理智。 :)
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多