ActiveRecord Rails 3 范围与类方法

Posted

技术标签:

【中文标题】ActiveRecord Rails 3 范围与类方法【英文标题】:ActiveRecord Rails 3 scope vs class method 【发布时间】:2011-08-19 11:26:49 【问题描述】:

我是 ActiveRecord 新查询界面的新手,所以我还在搞清楚。

我希望有人能解释在 ActiveRecord 模型中使用 scope 和仅使用类方法(即 self.some_method)之间的区别

据我所知,作用域总是期望返回关系,而类方法不一定必须返回。这是真的吗?

例如,我认为这样做是有意义的:

class Person
  scope :grouped_counts, group(:name).count
end

但这不起作用。我收到此错误:

ArgumentError: Unknown key(s): communicating, failed, matched, unmatched
    from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activesupport-3.0.5/lib/active_support/core_ext/hash/keys.rb:43:in `assert_valid_keys'
    from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activerecord-3.0.5/lib/active_record/relation/spawn_methods.rb:110:in `apply_finder_options'
    from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/activerecord-3.0.5/lib/active_record/named_scope.rb:110:in `block in scope'
    from (irb):48
    from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands/console.rb:44:in `start'
    from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands/console.rb:8:in `start'
    from /Users/bradrobertson/.rvm/gems/ruby-1.9.2-p180@influitive/gems/railties-3.0.5/lib/rails/commands.rb:23:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'
r

它确实可以作为类方法工作

def self.grouped_counts
  group(:name).count
end

我很想知道人们对何时使用范围以及何时使用类方法的想法。我是否正确假设范围必须始终返回一个关系,但一个类方法可以返回它想要的任何东西?

【问题讨论】:

【参考方案1】:

Rails 2.x 有更多的不同,因为 named_scopes 不执行你的查询(所以你可以链接它们),而类方法通常会执行查询(所以你不能链接它们),除非你手动将您的查询包装在scoped(...) 电话中。

在 Rails 3 中,一切都返回 ActiveRecord::Relation 直到您需要实际结果,因此范围可以与类方法链接,反之亦然(只要类方法返回 ActiveRecord::Relation 对象,而不是其他一些对象类型(像计数))。

通常,我使用scope 条目作为简单的单行词来过滤我的结果集。但是,如果我在“范围”内做任何复杂的事情,可能需要详细的逻辑、lambda、多行等,我更喜欢使用类方法。如你所见,如果我需要返回计数或类似的东西,我会使用类方法。

【讨论】:

优秀的答案。也见这篇文章:Named Scopes Are Dead【参考方案2】:

正如Dylan 在他的回答中提到的那样,作用域和类方法之间的一个区别是作用域是在加载类时评估的。这可能会导致意想不到的结果。

例如,

class Post < ActiveRecord::Base
    scope :published_earlier, where('published_at < ?', Date.today)
end

容易出错。正确的方法是使用 lambda

class Post < ActiveRecord::Base
    scope :published_earlier, ->  where('published_at < ?', Date.today) 
end

Lambda 块被延迟评估。因此 Date.today 在您调用范围时运行,而不是 评估类时。

如果使用类方法,则不需要使用 lambda。

class Post < ActiveRecord::Base
    def self.published_earlier
        where('published_at < ?', Date.today)
    end
end

因为是类方法,所以代码是在方法调用的时候运行的。

【讨论】:

需要注意的是,在 Rails 4 中,所有作用域都需要 lambda 形式。

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

如何使用 Arel(大概)创建一个不影响 Rails 3 中的查询的 ActiveRecord 范围?

rails 3.0中的多个范围

rails 范围检查关联是不是不存在

Rails:将复杂的 SQL 查询转换为 Arel 或 ActiveRecord

Rails范围过滤元素没有has_many关联元素

Rails 3 包括翻译 globalize3 activerecord