3

来自《使用 Rails 进行敏捷 Web 开发》一书

class Order < ActiveRecord::Base
   named_scope :last_n_days, lambda { |days| {:conditions =>
      ['updated < ?' , days] } }

   named_scope :checks, :conditions => {:pay_type => :check}
end

该声明

orders = Orders.checks.last_n_days(7)

将导致仅对数据库进行一次查询。

Rails 如何实现这一点?我是 Ruby 的新手,我想知道是否有一个特殊的结构允许这种情况发生。

为了能够像这样链接方法,由 named_scope 生成的函数必须返回它们自己或一个对象,而不是可以进一步限定范围。但是 Ruby 怎么知道这是最后一个函数调用并且它现在应该查询数据库呢?

我问这个是因为上面的语句实际上是查询数据库,而不仅仅是返回由链接产生的 SQL 语句。

4

3 回答 3

7

在 named_scope 魔术中有两个技巧(或模式,如果你愿意的话)。

代理模式- 在类或关联上调用命名范围方法总是返回 ActiveRecord::NamedScope::Scope 类的实例,而不是过滤后的 AR 对象的集合。这种模式虽然非常有用,但有时会使事情变得有点模糊,因为代理对象在本质上是矛盾的。

延迟加载- 由于延迟加载(在这种情况下意味着 - 仅在必要时才访问数据库)命名范围可以链接到您需要使用范围定义的集合时。每当您请求基础集合时,都会评估所有链式作用域并执行数据库查询。

最后一点:在 IRB 中使用命名作用域(或任何使用某种委托的东西)时要记住一件事。每次您按 Enter 键时,都会评估您事先编写的内容,并inspect在返回值上调用该方法。在链式命名作用域的情况下,尽管整个表达式被评估为一个作用域实例,但当 IRB 调用它的inspect方法时,作用域被评估并触发数据库查询。这是由于该inspect方法是通过委托传播到所有范围对象直至底层集合这一事实造成的。

于 2010-04-09T19:29:00.463 回答
3

你可能想试试这个

class Order < ActiveRecord::Base

  class << self
    def last_n_days(n)
      scoped(:conditions => ['updated < ?', days])
    end
    def checks
      scoped(:conditions => {:pay_type => :check})
    end
  end

end

用法是一样的

@orders = Order.last_n_days(5)
@orders = Order.checks
@orders = Order.checks.last_n_days(5)

这仍然可以完成您喜欢的所有延迟加载。也就是说,在您尝试访问记录之前,它不会进行查询。奖励:兼容 Rails 3!

命名范围已死

于 2010-04-10T05:47:57.177 回答
0

很酷。我正在考虑在 Javascript 中做这样的事情,但 Javascript 的行为相当奇怪。

该声明:

var x = SomeObject;

不调用 SomeObject 的 toString() 函数。但声明:

var x;
x = SomeObject;

按预期正确调用 toString() 函数。

这可以防止 Javascript 通过链接做一些很酷的事情。=(

于 2010-04-09T14:02:14.680 回答