主动记录关系的行为

时间:2013-10-24 12:07:15

标签: ruby-on-rails ruby activerecord

我遇到了有关activerecord关系的有趣行为。

class A < ActiveRecord::Base
  def self.one
    where("1=1")
  end

  def self.two
    puts A.where("2=2").to_sql
  end

  def self.test
     one.two
  end
end
A.test # prints  ... WHERE 1=1 AND 2=2

我预计Ad.where(“2 = 2”)在self.two中不包含“1 = 1”,但确实如此。它的特色吗?

1 个答案:

答案 0 :(得分:2)

是的,这是一项功能。

传递给where的所有值都由关系(@values[:where])内部存储,并与AND连接以构建WHERE语句。

two返回的关系的上下文中调用one时,将一个存储的“where values”用作构建新范围的基础。

您可以使用unscoped获得所需的行为:

one.unscoped.two

编辑:是的,这显然很奇怪且反直觉。

我和你有同样的问题。

经过大量挖掘后,似乎自rails 4以来,所有模型都保留了thread-local registry,用于存储该类/关系中使用的当前范围。我不知道他们为什么这么做,我的猜测是它与性能有关

关键是,当你已经链接了一堆范围并尝试调用引用该类的另一个范围时(例如你的two),ActiveRecord只是获取该类的当前范围 - 即,你首先调用范围的关系!很傻。实际上,您的two方法可以在没有A类引用的情况下重写,它不会改变任何内容。

Weirder和weirder,在使用STI时出现了另一个问题:在我的范围内,我使用pluck对父模型进行了完全无关的调用。不仅这个“功能”以意想不到的方式影响了查询,但它似乎也“重置”了默认范围,因此下一个查询(这是范围的实际返回)“忘记”了之前传递的所有条件范围... WTF?

结论:我认为我们应该在rails github上打开一张票,至少要求团队记录这种奇怪的行为。 (在我的第一个查询中使用MyClass.unscoped解决了我的问题,但不直观)