ActiveRecord Rails 3范围与类方法

bra*_*rad 50 activerecord class-method scopes ruby-on-rails-3

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

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

从我可以收集的内容来看,范围总是希望返回一个关系,而一个类方法不一定必须.这是真的?

例如,我认为做以下事情是有意义的:

class Person
  scope :grouped_counts, group(:name).count
end
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

但它确实可以作为一种类方法

def self.grouped_counts
  group(:name).count
end
Run Code Online (Sandbox Code Playgroud)

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

Dyl*_*kow 83

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

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

通常,我使用scope简单的单行条目来过滤我的结果集.但是,如果我在"范围"中做任何复杂的事情,可能需要详细的逻辑,lambdas,多行等,我更喜欢使用类方法.当你抓到,如果我需要返回计数或类似的东西,我使用类方法.


Zac*_* Xu 12

正如Dylan在他的回答中提到的,范围和类方法之间的一个区别是在加载类时会对范围进行评估.这可能会导致意外结果.

例如,

class Post < ActiveRecord::Base
    scope :published_earlier, where('published_at < ?', Date.today)
end
Run Code Online (Sandbox Code Playgroud)

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

class Post < ActiveRecord::Base
    scope :published_earlier, -> { where('published_at < ?', Date.today) }
end
Run Code Online (Sandbox Code Playgroud)

Lambda块被懒惰地评估.因此,在调用范围时运行Date.today,而不是在评估类时运行.

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

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

因为使用类方法,代码在方法调用时运行.

  • 应该注意的是,在Rails 4中,所有范围都需要lambda形式. (3认同)