Comment puis-je faire un filtrage par ensemble de requêtes Django différent?


666

Dans le modèle Django QuerySets, je vois qu'il y a un __gtet __ltpour les valeurs comparatives, mais y a-t-il un __ne/ !=/ <>( pas égal ?)

Je veux filtrer en utilisant un pas égal à:

Exemple:

Model:
    bool a;
    int x;

Je voudrais

results = Model.objects.exclude(a=true, x!=5)

La !=syntaxe n'est pas correcte. J'ai essayé __ne, <>.

J'ai fini par utiliser:

results = Model.objects.exclude(a=true, x__lt=5).exclude(a=true, x__gt=5)

75
Les résultats = Model.objects.exclude (a = true) .filter (x = 5) auraient-ils fonctionné?
hughdbrown le

3
@hughdbrown. Non. Votre requête exclut tout d' a=trueabord, puis applique le x=5filtre sur les autres. La requête prévue ne nécessitait que ceux avec a=trueet x!=5. La différence étant que tous ceux qui ont a=trueet x=5sont également filtrés.
Mitchell van Zuylen

Réponses:


690

Peut-être que les objets Q pourraient être utiles pour ce problème. Je ne les ai jamais utilisés, mais il semble qu'ils puissent être annulés et combinés un peu comme les expressions python normales.

Mise à jour: je viens de l'essayer, cela semble assez bien fonctionner:

>>> from myapp.models import Entry
>>> from django.db.models import Q

>>> Entry.objects.filter(~Q(id = 3))

[<Entry: Entry object>, <Entry: Entry object>, <Entry: Entry object>, ...]

16
@ JCLeitão: voir aussi la réponse de @ d4nt ci-dessous pour une syntaxe plus intuitive.
Paul D. Waite

612

Votre requête semble avoir un double négatif, vous voulez exclure toutes les lignes où x n'est pas 5, donc en d'autres termes, vous voulez inclure toutes les lignes où x EST 5. Je pense que cela fera l'affaire.

results = Model.objects.filter(x=5).exclude(a=true)

Pour répondre à votre question spécifique, il n'y a pas de "pas égal à", mais c'est probablement parce que django a à la fois des méthodes "filtrer" et "exclure" disponibles, vous pouvez donc toujours changer de logique pour obtenir le résultat souhaité.


2
@ d4nt: Je peux me tromper, mais je pense que la requête devrait êtreresults = Model.objects.filter(a=true).exclude(x=5)
Taranjeet

1
@Taranjeet: Je pense que vous avez mal lu la requête d'origine. La version de d4nt est correcte, car OP voulait exclure (a = True) et annuler l'exclusion de x = 5 (c'est-à-dire l'inclure).
Chuck

3
Je pense que c'est faux car une instance (x = 4, a = false) serait à tort exclue.
RemcoGerlich

4
@danigosa Cela ne semble pas correct. Je viens de l'essayer moi-même, et l'ordre des appels excludeet les filterappels n'ont fait aucune différence significative. L'ordre des conditions dans la WHEREclause change, mais comment est-ce important?
coredumperror

4
L'ordre d'exclusion et de filtrage @danigosa n'a pas d'importance.
EralpB

132

la field=valuesyntaxe dans les requêtes est un raccourci pour field__exact=value. C'est-à-dire que Django place les opérateurs de requête sur les champs de requête dans les identifiants . Django prend en charge les opérateurs suivants:

exact
iexact
contains
icontains
in
gt
gte
lt
lte
startswith
istartswith
endswith
iendswith
range
year
month
day
week_day
isnull
search
regex
iregex

Je suis sûr qu'en les combinant avec les objets Q comme le suggère Dave Vogt et en utilisant filter()ou exclude()comme Jason Baker le suggère, vous obtiendrez exactement ce dont vous avez besoin pour à peu près n'importe quelle requête possible.


merci c'est génial. j'ai utilisé quelque chose comme ça tg=Tag.objects.filter(user=request.user).exclude(name__regex=r'^(public|url)$')et ça marche.
suhailvs

@suhail, veuillez noter que toutes les bases de données ne prennent pas en charge cette syntaxe regex :)
Anoyz

2
i in icontains, iexactet similaire signifie «ignorer la sensibilité à la casse». Ce n'est pas pour "inverse".
Ivy Growing

Il convient de noter que lorsque vous utilisez exclude()plusieurs termes, vous souhaiterez peut-être composer la proposition avec l' ORopérateur, par exemple exclude(Q(field1__queryop1=value1) | Q(field2__queryop2=value2))afin d'exclure les résultats dans les deux conditions.
clapas du

98

Il est facile de créer une recherche personnalisée avec Django 1.7. Il existe un __neexemple de recherche dans la documentation officielle de Django .

Vous devez d'abord créer la recherche elle-même:

from django.db.models import Lookup

class NotEqual(Lookup):
    lookup_name = 'ne'

    def as_sql(self, qn, connection):
        lhs, lhs_params = self.process_lhs(qn, connection)
        rhs, rhs_params = self.process_rhs(qn, connection)
        params = lhs_params + rhs_params
        return '%s <> %s' % (lhs, rhs), params

Ensuite, vous devez l'enregistrer:

from django.db.models.fields import Field
Field.register_lookup(NotEqual)

Et maintenant, vous pouvez utiliser la __nerecherche dans vos requêtes comme ceci:

results = Model.objects.exclude(a=True, x__ne=5)

88

Dans Django 1.9 / 1.10, il y a trois options.

  1. Chaîne excludeetfilter

    results = Model.objects.exclude(a=true).filter(x=5)
  2. Utiliser des Q()objets et l' ~opérateur

    from django.db.models import Q
    object_list = QuerySet.filter(~Q(a=True), x=5)
  3. Enregistrer une fonction de recherche personnalisée

    from django.db.models import Lookup
    from django.db.models.fields import Field
    
    @Field.register_lookup
    class NotEqual(Lookup):
        lookup_name = 'ne'
    
        def as_sql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return '%s <> %s' % (lhs, rhs), params

    Le register_lookupdécorateur a été ajouté dans Django 1.8 et permet une recherche personnalisée comme d'habitude:

    results = Model.objects.exclude(a=True, x__ne=5)

1
object_list = QuerySet.filter (~ Q (a = True), x = 5): N'oubliez pas de conserver toutes les autres conditions ne contenant pas Q après celles contenant Q.
Bhumi Singhal

1
@MichaelHoffmann: A) vous filtrerez ensuite sur un plus petit ensemble de données après exclusion en utilisant ~ Q donc c'est plus efficace. B) le séquencement dans l'autre sens ne fonctionne probablement pas .. dun sais .. dun souviens-toi!
Bhumi Singhal

41

Alors que les modèles, vous pouvez filtrer avec =, __gt, __gte, __lt, __lte, vous ne pouvez pas utiliser ne, !=ou <>. Cependant, vous pouvez obtenir un meilleur filtrage lors de l'utilisation de l'objet Q.

Vous pouvez éviter de chaîner QuerySet.filter()et QuerySet.exlude(), et utiliser ceci:

from django.db.models import Q
object_list = QuerySet.filter(~Q(field='not wanted'), field='wanted')

24

Décision de conception en attente. Pendant ce temps, utilisezexclude()

Le tracker de problème Django a l' entrée remarquable # 5763 , intitulée "Queryset n'a pas d'opérateur de filtre" différent " . Il est remarquable car (en avril 2016) il a été "ouvert il y a 9 ans" (à l'âge de pierre de Django), "fermé il y a 4 ans", et "modifié depuis 5 mois".

Lisez la discussion, c'est intéressant. Fondamentalement, certaines personnes soutiennent __nequ'il faudrait ajouter tandis que d'autres disent que exclude()c'est plus clair et ne__ne devrait donc pas être ajouté.

(Je suis d'accord avec le premier, car le dernier argument équivaut à peu près à dire que Python ne devrait pas avoir !=parce qu'il l'a fait ==et notdéjà ...)


22

Utilisation d'exclusion et de filtre

results = Model.objects.filter(x=5).exclude(a=true)

18

Vous devez utiliser filteret excludeaimer ceci

results = Model.objects.exclude(a=true).filter(x=5)

8

Le dernier bit de code exclura tous les objets où x! = 5 et a est True. Essaye ça:

results = Model.objects.filter(a=False, x=5)

N'oubliez pas que le signe = dans la ligne ci-dessus attribue la valeur False au paramètre a et le nombre 5 au paramètre x. Il ne s'agit pas de vérifier l'égalité. Ainsi, il n'y a vraiment aucun moyen d'utiliser le symbole! = Dans un appel de requête.


3
Ce n'est pas 100% la même chose car il pourrait également y avoir des valeurs nulles pour ces champs.
MikeN

Cela renvoie uniquement les éléments qui ont a = False et x = 5, mais dans la question, une instance (a = false, x = 4) serait incluse.
RemcoGerlich

1
results = Model.objects.filter(a__in=[False,None],x=5)
Jeremy

8

results = Model.objects.filter (a = True) .exclude (x = 5)
Génère ce sql:
sélectionnez * dans le tableaux où a! = 0 et x! = 5
Le sql dépend de la façon dont votre champ True / False est représenté et du moteur de base de données. Le code django est cependant tout ce dont vous avez besoin.



6

Ce que vous recherchez, ce sont tous les objets qui ont soit a=false ou x=5 . Dans Django, |sert d' ORopérateur entre les ensembles de requêtes:

results = Model.objects.filter(a=false)|Model.objects.filter(x=5)

5

Cela donnera le résultat souhaité.

from django.db.models import Q
results = Model.objects.exclude(Q(a=True) & ~Q(x=5))

pour pas égal, vous pouvez utiliser ~sur une requête égale. évidemment, Qpeut être utilisé pour atteindre la requête égale.


Veuillez vérifier l'édition; utiliser «et» dans Q(a=True) and ~Q(x=5)serait évalué ~Q(x=5)comme argument pour .exclude. Veuillez lire: docs.python.org/3/reference/expressions.html#boolean-operations et docs.python.org/3/reference/… .
tzot

2

Attention aux nombreuses réponses incorrectes à cette question!

La logique de Gerard est correcte, bien qu'elle renvoie une liste plutôt qu'un ensemble de requêtes (ce qui peut ne pas avoir d'importance).

Si vous avez besoin d'un ensemble de requêtes, utilisez Q:

from django.db.models import Q
results = Model.objects.filter(Q(a=false) | Q(x=5))
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.