2011-05-05 103 views
50

我是新來的ActiveRecord的新查詢接口,所以我仍然搞清楚事情。ActiveRecord Rails 3範圍vs類方法

我希望有人能解釋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/[email protected]/gems/activesupport-3.0.5/lib/active_support/core_ext/hash/keys.rb:43:in `assert_valid_keys' 
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/activerecord-3.0.5/lib/active_record/relation/spawn_methods.rb:110:in `apply_finder_options' 
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/activerecord-3.0.5/lib/active_record/named_scope.rb:110:in `block in scope' 
    from (irb):48 
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/railties-3.0.5/lib/rails/commands/console.rb:44:in `start' 
    from /Users/bradrobertson/.rvm/gems/[email protected]/gems/railties-3.0.5/lib/rails/commands/console.rb:8:in `start' 
    from /Users/bradrobertson/.rvm/gems/[email protected]/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 

我想知道人民在何時使用範圍的想法以及何時使用類方法的工作。我認爲範圍必須總是返回一個關係,但是一個類方法可以返回任何想要的結果嗎?

回答

81

由於named_scopes沒有執行你的查詢(所以你可以鏈接它們),而類方法通常會執行查詢(所以你不能鏈接它們),除非你有更多的Rails 2.x差異手動將您的查詢包裝在scoped(...)調用中。在您需要實際結果之前,所有內容都會返回一個ActiveRecord::Relation,因此範圍可以與類方法鏈接,反之亦然(只要類方法返回ActiveRecord::Relation對象,而不是其他某些對象類型(如count ))。

通常,我使用scope條目進行簡單的一行來篩選我的結果集。但是,如果我在可能需要詳細邏輯,lambda表達式,多行等的「範圍」中執行任何複雜操作,我寧願使用類方法。當你發現,如果我需要返回計數或類似的東西,我使用類方法。

+0

酷,thx爲輸入! – brad 2011-05-05 15:46:24

+0

優秀的答案。也看到這篇文章:[命名的範圍已經死了](http://www.railway。at/2010/03/09/named-scopes-are-dead /) – mjnissim 2013-02-09 19:50:32

10

由於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 

因爲使用類方法,代碼在方法調用時運行。

+2

應該注意的是,在Rails 4中,lambda表單對於所有範圍都是必需的。 – pdobb 2014-02-21 03:36:47