2009-12-29 42 views
0
named_scope :with_country, lambad { |country_id| ...} 

named_scope :with_language, lambad { |language_id| ...} 

named_scope :with_gender, lambad { |gender_id| ...} 

if params[:country_id] 
    Event.with_country(params[:country_id]) 
elsif params[:langauge_id] 
    Event.with_state(params[:language_id]) 
else 
    ...... 
    #so many combinations 
end 

如果我同時使用國家和語言,那麼我需要同時應用它們。在我的真實應用程序中,我有8個不同的named_scopes,可以根據情況應用。如何逐步應用named_scope或在某處保留named_scopes,然後稍後應用一次。如何在Rails中增量應用named_scopes

我想抱着一種價值觀這樣

tmp = Event.with_country(1) 

但跑動的SQL。

我想我可以寫類似

if !params[:country_id].blank? && !params[:language_id].blank? && !params[:gender_id].blank? 
    Event.with_country(params[:country_id]).with_language(..).with_gender 
elsif country && language 
elsif country && gender 
elsif country && gender 
.. you see the problem 

回答

2

實際上,SQL不會立即觸發。儘管我沒有仔細查看Rails如何取消這種魔術(儘管我現在很好奇),但只有在您真正檢查了結果集的內容之後,查詢纔會被解僱。

所以,如果你運行在控制檯以下:

wc = Event.with_country(Country.first.id);nil # line returns nil, so wc remains uninspected 
wc.with_state(State.first.id) 

你會注意到,沒有任何事件查詢解僱的第一線,而一個大的事件查詢時,會引發第二。因此,您可以安全地將Event.with_country(params[:country_id])作爲變量存儲,並在稍後向其添加更多範圍,因爲查詢只會在最後被觸發。

要確認這是真的,請嘗試我描述的方法,並檢查您的服務器日誌以確認只有一個查詢在頁面上觸發事件。

+0

如果你刪除零,那麼你可以看到,這是SQL立即被解僱 – Roger 2009-12-29 16:38:43

+0

我的壞。腳本/控制檯調用to_s,這就是爲什麼sql被炒魷魚 – Roger 2009-12-29 16:44:41

+0

Ayup。 nil確保to_s從未發生過:) 既然您知道您可以將範圍堆疊在一起,那麼您的問題是否已得到回答? – Matchu 2009-12-29 22:06:24

1

檢查Anonymous Scopes

+0

我已經更新了我的問題,這樣你可以更清楚地看到這個問題。 – Roger 2009-12-29 15:46:33

+0

更新了答案。 – khelll 2009-12-29 16:09:13

0

Event.with_country(params[:country_id]).with_state(params[:language_id])

會工作,將不發射SQL直到結束(如果你嘗試在控制檯,它會馬上發生,因爲控制檯將呼籲的結果to_s。IRL的SQL贏得直到結束纔開火)。

我懷疑你還需要確保每個named_scope測試什麼傳入的存在:

named_scope :with_country, lambda { |country_id| country_id.nil? ? {} : {:conditions=>...} } 
0

這將是很容易使用Rails 3:

products = Product.where("price = 100").limit(5) # No query executed yet 
products = products.order("created_at DESC")  # Adding to the query, still no execution 
products.each { |product| puts product.price } # That's when the SQL query is actually fired 

class Product < ActiveRecord::Base 
    named_scope :pricey, where("price > 100") 
    named_scope :latest, order("created_at DESC").limit(10) 
end 
0

簡短的回答是根據需要簡單地改變示波器的範圍,根據所存在的參數縮小範圍:

scope = Example 

# Only apply to parameters that are present and not empty 
if (!params[:foo].blank?) 
    scope = scope.with_foo(params[:foo]) 
end 

if (!params[:bar].blank?) 
    scope = scope.with_bar(params[:bar]) 
end 

results = scope.all 

更好的方法是使用像Searchlogic(http://github.com/binarylogic/searchlogic)這樣的封裝所有這些給你。

1

我不得不做類似的事情,在視圖中應用了許多過濾器。我所做的是創造named_scopes與條件:

named_scope :with_filter, lambda{|filter| { :conditions => {:field => filter}} unless filter.blank?} 

在同一個班級有從操作接收PARAMS和返回篩選記錄的方法:

def self.filter(params) 
    ClassObject 
    .with_filter(params[:filter1]) 
    .with_filter2(params[:filter2]) 
end 

就像你可以添加所有使用named_scopes的過濾器,它們根據發送的參數使用。

我把這個想法從這裏:http://www.idolhands.com/ruby-on-rails/guides-tips-and-tutorials/add-filters-to-views-using-named-scopes-in-rails