【问题标题】:Ruby on Rails ActiveRecord scopes vs class methodsRuby on Rails ActiveRecord 范围与类方法
【发布时间】:2025-12-12 12:20:13
【问题描述】:

Rails 在内部将作用域转换为类方法,那么为什么我们不能使用类方法本身而不是使用作用域。

【问题讨论】:

标签: ruby-on-rails ruby rails-activerecord


【解决方案1】:

来自fine guide

14 个作用域
[...]
为了定义一个简单的作用域,我们在类中使用scope 方法,传递我们希望在调用这个作用域时运行的查询:

class Article < ActiveRecord::Base
  scope :published, -> { where(published: true) }
end

这和定义一个类方法完全一样,使用哪个看个人喜好:

class Article < ActiveRecord::Base
  def self.published
    where(published: true)
  end
end

特别注意:

这和定义一个类方法完全一样,使用哪个看个人喜好

还有一个little further(顺便说一句,Rails3 指南在这里说同样的话):

14.1 传入参数
[...]
使用类方法是接受范围参数的首选方式。

因此,您使用哪个是一个偏好问题,甚至建议您将类方法用于带参数的作用域。

使用scope 主要是一个符号问题。如果您说scope :whatever,那么您明确表示whatever 是一个查询构建器;如果你说def self.whatever,那么你并没有暗示任何关于whatever 方法的意图,你只是在定义一些可能表现得像或可能不像作用域的类方法。

当然,14.1 建议您在作用域接受参数时不要使用scope,从而弄乱了这种符号区别。还要记住,在 Rails3 中你可以说:

scope :published, where(published: true)

所以无参数作用域在视觉上是“干净”和简洁的,但是添加一个 lambda 来处理参数会使其看起来更混乱:

scope :pancakes, ->(x) { where(things: x) }

但是 Rails4 需要 lambda,即使是对于无参数作用域,这种区别现在更没有意义了。

我怀疑目前的差异是历史性的。 Scopes 在以前可能是特别的东西,但在 Rails3 时代变成了普通的旧类方法,以减少重复并更好地与 Rails3 附带的新查询接口相结合。


因此,如果您愿意,您可以跳过scope 并直接进入类方法。当您的范围接受参数时,我们甚至鼓励您这样做。

【讨论】:

    【解决方案2】:

    Scopes 只是class methods. Active Record 在内部将作用域转换为类方法。

    “它们之间没有区别”或“只是口味问题”。我倾向于同意这两个句子,但我想说明两者之间存在的一些细微差别。 这个blogs 很好地解释了不同之处。

    【讨论】: