0

I have a relation between two objects. Let's say it like this: Model1 has_many Model2 (That doesn't really matter)

And say, I want to filter-out some of the results:

a = Model1.find(123) 
b = a.model2

And now, for example, I want to select only EVEN records (by ID)

If I do following: b.select {|x| x.id % 2 == 0} then it returns all even records as expected. And NO additional database queries created.

But if I define a class method in the Model2:

def self.even_records
   select {|x| x.id % 2 == 0}
end

Then, for some magic reason it makes an additional query to database, that looks like it re-instantiated the "b" variable (re-loads the relation):

Model2 Load (0.4ms)  SELECT `model2`.* FROM `model2` WHERE `model2`.`model1_id` = 123

Why it behaves so ? Is there any way I can fix it ?

P.S I have no fishy callbacks, like after_find or whatsoever defined in any of models.

4

2 回答 2

2

ActiveRecord作用域是惰性求值的,即作用域在需要其结果时进行求值。当您在控制台中尝试此代码时,inspect会在每个评估对象上隐式调用方法,包括ActiveRecord::Relation从返回的实例

b = a.model2

称呼。调用inspecton后ActiveRecord::Relation,将评估范围并创建数据库查询,因为有必要inspect正确显示返回值。

相反,当您在 rails 控制台之外运行代码时,

b = a.model2

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

于 2013-10-01T08:42:46.983 回答
1

这两者之间的基本区别在于,当您在数组 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?and的方法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。谢谢

于 2013-10-01T08:40:11.873 回答