2009-12-29 5 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 

Si j'obtiens à la fois le pays et la langue, alors je dois appliquer les deux. Dans ma vraie application j'ai 8 named_scopes différents qui pourraient être appliqués selon le cas. Comment appliquer named_scopes de manière incrémentielle ou conserver sur named_scopes quelque part, puis appliquer plus tard en un seul coup.Comment appliquer named_scopes de manière incrémentale dans Rails

J'ai essayé tenant à des valeurs telles que ce

tmp = Event.with_country(1) 

mais qui se déclenche instantanément l'sql.

Je suppose que je peux écrire quelque chose comme

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 

Répondre

2

En fait, le SQL ne se déclenche pas instantanément. Bien que je n'aie pas pris la peine de chercher comment Rails réussit cette magie (bien que je sois curieux), la requête n'est pas lancée tant que vous n'avez pas inspecté le contenu du résultat.

Donc, si vous exécutez la commande suivante dans la console:

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

vous remarquerez qu'aucune requête d'événement est déclenché pour la première ligne, alors qu'une grande requête d'événement est déclenché pour la seconde. En tant que tel, vous pouvez stocker en toute sécurité Event.with_country(params[:country_id]) en tant que variable et y ajouter plus d'étendues ultérieurement, car la requête ne sera déclenchée qu'à la fin. Pour confirmer que cela est vrai, essayez l'approche que je décris et vérifiez vos journaux de serveur pour confirmer qu'une seule requête est lancée sur la page elle-même pour les événements.

+0

Si vous supprimez zéro alors vous pouvez voir que sql est déclenché instantanément – Roger

+0

mon mauvais. script/console appelle to_s et c'est pourquoi sql se fait virer – Roger

+0

Ayup. Le zéro était pour s'assurer que les to_s ne se sont jamais produits :) Maintenant que vous savez que vous pouvez empiler des oscilloscopes les uns sur les autres, avez-vous répondu à votre question? – Matchu

1

Vérifier Anonymous Scopes.

+0

J'ai mis à jour ma question afin que vous puissiez voir le problème plus clairement. – Roger

+0

Mise à jour de la réponse. – khelll

0

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

fonctionnera et ne se déclenche pas SQL jusqu'à la fin (si vous l'essayez dans la console, il va se produire tout de suite parce que la console appellera to_s sur les résultats. IRL SQL gagné pas de tir jusqu'à la fin).

Je vous soupçonnez devez également être sûr que chaque named_scope teste l'existence de ce qui est passé dans:

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

Ce sera facile avec 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

La réponse courte est à il suffit de décaler la portée selon les besoins, en la réduisant selon les paramètres présents:

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 

Une meilleure approche serait d'utiliser quelque chose comme Searchlogic (http://github.com/binarylogic/searchlogic) qui encapsule tout cela pour vous.

1

Je devais faire quelque chose de similaire, ayant beaucoup de filtres appliqués dans une vue.Ce que je faisais était de créer named_scopes avec des conditions:

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

Dans la même classe il existe une méthode qui reçoit les params de l'action et renvoie les enregistrements filtrés:

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

Comme vous pouvez ajouter tous les filtres utilisant named_scopes et ils sont utilisés en fonction des params qui sont envoyés.

Je pris l'idée d'ici: http://www.idolhands.com/ruby-on-rails/guides-tips-and-tutorials/add-filters-to-views-using-named-scopes-in-rails

Questions connexes