Rails 3中的范围与类方法

Posted

技术标签:

【中文标题】Rails 3中的范围与类方法【英文标题】:Scope vs Class Method in Rails 3 【发布时间】:2012-02-18 23:37:33 【问题描述】:

基于 Rails 3 API,作用域和类方法之间几乎没有区别。

class Shipment < ActiveRecord::Base
  def self.unshipped
    where(:shipped => false)
  end
end

相同
scope :unshipped, where(:shipped => false)

但是,我发现使用它们有时会得到不同的结果。

虽然它们都生成相同、正确的 SQL 查询,但作用域在调用时似乎并不总是返回正确的值。看起来这个问题只有在方法中以相同的方式调用两次时才会发生,尽管是在不同的货物上。第二次调用它时,当使用作用域时,它返回与第一次相同的东西。而如果我使用类方法,它可以正常工作。

使用范围时是否会发生某种查询缓存?

编辑:

order.line_items.unshipped

上面一行是作用域的调用方式。订单有很多 line_items。

generate_multiple_shipments 方法被调用了两次,因为该测试会创建一个订单并生成发货以查看有多少。然后它会更改订单并重新生成发货。但是,group_by_ship_date 返回的结果与订单的第一次迭代相同。

def generate_multiple_shipments(order)
  line_items_by_date = group_by_ship_date(order.line_items.unshipped)

  line_items_by_date.keys.sort.map do |date|
    shipment = clone_from_order(order)
    shipment.ship_date = date
    line_items_by_date[date].each  |line_item| shipment.line_items << line_item 
    shipment
  end
end

def group_by_ship_date(line_items)    
  hash = 
  line_items.each do |line_item|
    hash[line_item.ship_date] ||= []
    hash[line_item.ship_date] << line_item
  end
  hash
end

【问题讨论】:

您能否提供一个示例(代码的 sn-p),您认为查询在被调用两次时会被缓存。 我在原帖中添加了更多信息。 检查 Rails 日志将显示结果是否被缓存。 【参考方案1】:

我认为您的调用不正确。你应该添加所谓的查询方法来执行范围,例如allfirstlast,即:

order.line_items.unshipped.all

我观察到一些不一致的地方,尤其是在 rspec 中,通过添加查询方法可以避免这些不一致。

您没有发布您的测试代码,所以很难准确地说,但我的经验是,在您修改关联记录后,您必须强制重新加载,因为查询缓存并不总是足够智能来检测改变。通过将true 传递给关联,您可以强制关联重新加载并重新运行查询:

order.line_items(true).unshipped.all

【讨论】:

强制关联重新加载似乎已经成功了。我仍然不完全清楚查询缓存如何确定是否发生了变化,但至少它现在正在工作。谢谢! @blim8183 我也不知道这个问题的答案,但我怀疑这可能是一种设计权衡。很高兴重新加载对您有用。【参考方案2】:

假设您引用的是 Rails 3.1,则作用域可能会受到模型上定义的默认作用域的影响,而类方法则不会。

【讨论】:

以上是关于Rails 3中的范围与类方法的主要内容,如果未能解决你的问题,请参考以下文章

如何修复弃用警告:类级别方法将不再继承 Rails 6.1 中的范围?

类语法规则与类中的函数

类与类之间的调用

Python类的特点 :静态方法与类方法

在类范围内声明一个与类属性同名的局部变量

第 4 章 对象与类