我有两个对象之间的关系。让我们这样说:Model1 has_many Model2(这并不重要)
并说,我想过滤掉一些结果:
a = Model1.find(123)
b = a.model2
现在,例如,我想只选择EVEN记录(按ID)
如果我执行以下操作:b.select {|x| x.id % 2 == 0}
则会按预期返回所有偶数记录。并且没有创建其他数据库查询。
但是如果我在Model2中定义一个类方法:
def self.even_records
select {|x| x.id % 2 == 0}
end
然后,由于某些神奇的原因,它对数据库进行了额外的查询,看起来它重新实例化了“b”变量(重新加载关系):
Model2 Load (0.4ms) SELECT `model2`.* FROM `model2` WHERE `model2`.`model1_id` = 123
为什么表现如此?有什么办法可以解决吗?
P.S我没有任何可疑的回调,例如after_find
或任何模型中定义的任何回调。
答案 0 :(得分:2)
这两者之间的基本区别在于,当你在b上调用select方法时,它是一个数组,而不是调用可枚举方法select。
b.select {|x| x.id % 2 == 0}
当你在一个方法中写入时,它会调用activerecord查询接口的select方法。
def self.even_records
select {|x| x.id % 2 == 0}
end
BTW Ruby有像even?
和odd?
这样的方法,所以你可以直接调用它们:
even_records = b.select{|x| x.id.even?}
odd_records = b.select{|x| x.id.odd? }
Edit:
我找到了一个简单的解决方案,您可以在模型Model2
中定义范围,如下所示,
scope :even_records, -> { where ('id % 2 == 0') }
现在如果你打电话:
Model2.even_records
你将获得even_records。 感谢
答案 1 :(得分:2)
ActiveRecord
范围被懒惰地评估,即在需要其结果时评估范围。在控制台中尝试此代码时,会在每个评估对象上隐式调用inspect
方法,包括从
ActiveRecord::Relation
实例
b = a.model2
呼叫。在inspect
上调用ActiveRecord::Relation
后,将评估范围并创建数据库查询,因为必须正确显示inspect
返回值。
相反,当您在rails控制台之外运行代码时,
b = a.model2
不会产生数据库查询,因此可能只有一个数据库查询。