我是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
我很想知道人们关于何时使用范围以及何时使用类方法的想法。我是否正确假设范围必须始终返回一个关系,但类方法可以返回它想要的任何内容?
答案 0 :(得分:82)
Rails 2.x中存在更多差异,因为named_scopes没有执行您的查询(因此您可以链接它们),而类方法通常会执行查询(因此您无法链接它们),除非您手动在scoped(...)
来电中包裹您的查询。
在Rails 3中,一切都返回ActiveRecord::Relation
,直到你需要实际的结果,因此范围可以链接到类方法,反之亦然(只要类方法返回ActiveRecord::Relation
个对象,而不是一些其他对象类型(如计数))。
通常,我使用scope
条目来简单的单行来过滤我的结果集。但是,如果我在&#34;范围内做任何复杂的事情&#34;这可能需要详细的逻辑,lambdas,多行等,我更喜欢使用类方法。当你抓到时,如果我需要返回计数或类似的东西,我会使用类方法。
答案 1 :(得分:11)
正如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
因为使用类方法,代码在方法调用时运行。