2010-11-15 20 views
4

de Django On suppose un modèleComment filtrer CommaSeparatedIntegerField

class Foo(models.Model): 
    bar = models.CommaSeparatedIntegerField('Filter Me!') 

Le contenu du bar pourrait ressembler, par exemple, 12,35,67,142.

Je veux interroger tous les Foo s, qui ont un 42 dans bar:

all_42_foos = Foo.objects.filter(bar__contains="42") 

qui ne donne pas le résultat correct, puisque CommaSeparatedIntegerField hérite de CharField et l'évaluation du filtre utilise le contenu de la chaîne de champ (correspondant à l'exemple ci-dessus avec 142, aussi).

Comment puis-je avoir un filtre, qui fait un .split(",") sur le champ bar avant de rechercher le 42? Je ne veux vraiment pas que bar devienne un ManyToMany, car ce serait un terrible frais généraux.

Répondre

12

Qu'en est-il quelque chose comme:

from django.db.models import Q 

all_42_foos = Foo.objects.filter(Q(bar__startswith='42,') | Q(bar__endswith=',42') | Q(bar__contains=',42,') | Q(bar__exact='42')) 

Bien qu'il soit un peu d'une requête détaillée, je pense que quelque chose le long de ces lignes sera la seule façon d'obtenir ce que vous cherchez. Il est probablement utile de transformer cela en une fonction distincte le long des lignes de

def all_x_foos(x): 
    return Foo.objects.filter(Q(bar__startswith=x+',') | Q(bar__endswith=','+x) | Q(bar__contains=',{0},'.format(x)) | Q(bar__exact=x)) 

Par curiosité, avez-vous vérifié la performance réelle de votre application w/à la fois le nombre à plusieurs sens, et le pseudo-many to-many méthode que vous décrivez?

+1

Merci! Oui, c'est bavard, mais ça fait l'affaire. Une remarque: il faut aussi tester un '42,' (virgule) exacte, puisque c'est aussi valable pour ce champ. – Boldewyn

0

Cette réponse est une extension de la réponse de @desfido. Découplage systématique du type d'objet de la fonction filtrant le champ CommaSeperatedInteger. Cette solution est plus rapide que l'utilisation d'une regex pour trouver la valeur correcte.J'ai utilisé cette regex.

_regex = r"(^|(\d*,)+)(%s)((,\d*)+|$)" %('|'.join(_ids)) 

Si vous souhaitez rechercher plusieurs valeurs à l'intérieur de la virgule valeur seperated, on peut appeler la fonction donnée ci-dessous dans une boucle

Ce arguments mot-clé de cette fonction sont: -

cs_field_name sera être le nom du champ entier séparé par des virgules.

x sera l'entier à rechercher.

Il retourne un objet Q, qui peuvent ensuite être combinés et utilisés pour le filtrage

def search_comma_seperated_field(cs_field_name,x): 
    startswith_key = cs_field_name + "__startswith" 
    endswith_key = cs_field_name + "__endswith" 
    contains_key = cs_field_name + "__contains" 
    exact_key = cs_field_name + "__exact" 
    return Q(**{startswith_key : x+','}) | \ 
            Q(**{endswith_key:','+x}) |\ 
            Q(**{contains_key : ',{0},'.format(x)}) |\ 
            Q(**{exact_key:x}) 
0

Vous pouvez également écrire dans une expression régulière:

all_42_foos = Foo.objects.filter(bar__regex=u'^42,|,42,|,42$|^42$') 

ou

bar_number = 42 
bar_regex = r'^{0},|,{0},|,{0}$|^{0}$'.format(bar_number) 
all_42_foos = Foo.objects.filter(bar__regex=bar_regex)