2009-06-01 10 views
21

Je suis habitué à Django où vous pouvez exécuter plusieurs méthodes de filtrage sur les jeux de requête, par exemple Item.all.filter(foo="bar").filter(something="else").Filtrage des requêtes ActiveRecord dans les rails

Cependant, ce n'est pas si facile à faire dans Rails. Item.find(:all, :conditions => ["foo = :foo", { :foo = bar }]) renvoie un tableau qui signifie cela ne fonctionnera pas:

Item.find(:all, :conditions => ["foo = :foo", { :foo = 'bar' }]).find(:all, :conditions => ["something = :something", { :something = 'else' }])

Alors je me suis le meilleur moyen de filtres « pile » est de modifier le tableau des conditions et puis exécutez la requête.

Alors je suis venu avec cette fonction:

def combine(array1,array2) 
    conditions = [] 
    conditions[0] = (array1[0]+" AND "+array2[0]).to_s 
    conditions[1] = {} 
    conditions[1].merge!(array1[1]) 
    conditions[1].merge!(array2[1]) 
    return conditions 
end 

Utilisation:

array1 = [ "foo =: ​​foo", {: 'bar' foo =}] array2 = [ "quelque chose =: quelque chose », {: quelque chose = 'else'}] = conditions se combinent (array1, array2) articles = Item.find (: all,: conditions => conditions)

Cela a fonctionné assez bien. Cependant, je veux être en mesure de combiner un nombre arbitraire de tableaux, ou essentiellement un raccourci pour l'écriture:

conditions = combine(combine(array1,array2),array3) 

Quelqu'un peut-il aider? Merci d'avance.

Répondre

35

Ce que vous voulez sont named scopes:

class Item < ActiveRecord::Base 
    named_scope :by_author, lambda {|author| {:conditions => {:author_id => author.id}}} 
    named_scope :since, lambda {|timestamp| {:conditions => {:created_at => (timestamp .. Time.now.utc)}}} 
    named_scope :archived, :conditions => "archived_at IS NOT NULL" 
    named_scope :active, :conditions => {:archived_at => nil} 
end 

Dans vos contrôleurs, utilisez comme ceci:

class ItemsController < ApplicationController 
    def index 
    @items = Item.by_author(current_user).since(2.weeks.ago) 
    @items = params[:archived] == "1" ? @items.archived : @items.active 
    end 
end 

L'objet retourné est un proxy et la requête SQL ne sera pas exécuté jusqu'à ce que vous avez réellement Commencer à faire quelque chose de réel avec la collection, comme l'itération (pour l'affichage) ou lorsque vous appelez les méthodes Enumerable sur le proxy.

+0

Oui, c'est exactement ce que je veux. J'ai regardé le screencast et cela m'a semblé logique. Mais dès que j'ai déclenché le code, j'ai reçu une erreur disant que la portée nommée était une méthode non définie. Ensuite, j'ai remarqué que je travaillais avec Rails 1.8, donc j'ai mis à jour les rails à 2.3.quelque chose et toujours eu la même erreur .... euh, je savais que les étendues nommées étaient trop belles pour être vraies:/ – user94154

+1

Si vous avez gelé Rails, alors votre code utilise toujours une version précédente de Rails. Rails 1.8 n'a jamais existé, vous devez donc dire Ruby 1.8. rails -v est une commande qui vous dira quelle version de Rails existe sur la ligne de commande. script/about vous en dira plus sur l'environnement de votre application. –

+0

bien le script ruby ​​/ à propos n'a pas été reconnu. Que veut dire congelé en termes de Rails? – user94154

1

Vous pouvez jeter un oeil à Searchlogic.
Il est plus facile d'utiliser les conditions sur ActiveRecord ensembles, et même sur Arrays.

Espérons que ça aide.

4

Je pense que ce que vous voulez est le suivant: Named Scope

1

Vous pouvez (ou tout au moins utilisé pour pouvoir) filtrer comme si Rails:

find(:all, :conditions => { :foo => 'foo', :bar => 'bar' }) 

où: foo et: bar sont champ noms dans l'enregistrement actif. On dirait que tout ce que vous devez faire est de passer un hash de: field_name => paires de valeurs.

+0

Cela a fonctionné pour moi (je suis sur le 1.8.7) – John

6

Je ne le ferais pas comme vous l'avez proposé.

Depuis trouver retourner un tableau, vous pouvez utiliser des méthodes de tableau pour filtrer, par exemple sur:

Item.find(:all).select {|i| i.foo == bar }.select {|i| i.whatever > 23 }... 

Vous pouvez également achive ce que vous voulez avec les étendues nommées.

+1

Les portées nommées sont bien meilleures pour ce faire et vous offrent des méthodes d'enchaînement facilement réutilisables que vous pouvez utiliser. – nitecoder

+1

@railsninja: Je suis d'accord, mais vous devez définir la portée nommée avant de l'utiliser. Si vous voulez faire quelque chose une seule fois, il est plus facile d'utiliser select. – klew

+2

Pas vrai. Vous pouvez faire des portées anonymes à la volée: http://railscasts.com/episodes/112-anonymous-scopes –

Questions connexes