【问题标题】:Rails class method scope makes an extra queryRails 类方法范围进行额外查询
【发布时间】:2013-10-07 07:50:57
【问题描述】:

我有两个对象之间的关系。让我们这样说:Model1 has_many Model2(这并不重要)

说,我想过滤掉一些结果:

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

为什么会这样?有什么办法可以解决吗?

P.S 我没有可疑的回调,例如 after_find 或任何模型中定义的任何回调。

【问题讨论】:

  • 如果定义b = a.model2,可以粘贴'b'对象的结果吗?
  • 你想“修复”什么?您是否在控制台中尝试此代码?
  • @RailsGuy 1.9.3p448 :186 > b => [#] b.class=> 数组
  • @MarekLipka 是的,在控制台中。控制台有问题吗?

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


【解决方案1】:

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

返回的 ActiveRecord::Relation 实例
b = a.model2

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

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

b = a.model2

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

【讨论】:

  • 感谢您的解释。
【解决方案2】:

这两者的基本区别在于,当你在数组 b 上调用 select 方法时,它调用的是可枚举方法 select

b.select {|x| x.id % 2 == 0} 

并且当你写一个方法时,它会调用activerecord查询接口的select方法。

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。 谢谢

【讨论】:

  • 啊。我知道了。但是,仍然可以使用 AR 查询接口,但没有任何额外的查询吗?我知道“甚至?” “奇怪?”方法。但这只是一个例子所以,您认为唯一的解决方案就是扩展 Array 类?
猜你喜欢
  • 1970-01-01
  • 2012-03-13
  • 2011-01-05
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多