我遇到了有关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”,但确实如此。它的特色吗?
答案 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
解决了我的问题,但不直观)