Rails类方法范围进行额外查询

时间:2013-10-01 08:25:02

标签: mysql sql ruby-on-rails ruby scope

我有两个对象之间的关系。让我们这样说: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或任何模型中定义的任何回调。

2 个答案:

答案 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

不会产生数据库查询,因此可能只有一个数据库查询。