我有两个对象之间的关系。让我们这样说:模型 1 has_many模型 2(这并不重要)
然后说,我想过滤掉一些结果:
a = Model1.find(123)
b = a.model2
现在,例如,我只想选择偶数记录(按 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
为什么它表现得如此?有什么方法可以解决吗?
附言我没有可疑的回调,例如after_find
或任何模型中定义的回调。
ActiveRecord
作用域被惰性地计算,即当需要其结果时,作用域被计算。在控制台中尝试此代码时,inspect
方法将在每个评估对象上隐式调用,包括从 返回ActiveRecord::Relation
实例
b = a.model2
叫。在ActiveRecord::Relation
上调用inspect
后,将评估范围并创建数据库查询,因为有必要正确显示inspect
返回值。
相反,当您在 rails 控制台之外运行代码时,
b = a.model2
不会生成数据库查询,因此可能只有一个数据库查询。
这两者之间的基本区别在于,当您在数组 b 上调用 select 方法时,它调用可枚举方法 select。
b.select {|x| x.id % 2 == 0}
当你在方法中编写时,它会调用 Select 方法的 ActiveRecord 查询接口。
def self.even_records
select {|x| x.id % 2 == 0}
end
顺便说一句,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。谢谢