Filtrage des noms vides ou NULL dans un ensemble de requêtes

463

J'ai first_name, last_name& alias(facultatif) que je dois rechercher. Donc, j'ai besoin d'une requête pour me donner tous les noms qui ont un ensemble d'alias.

Seulement si je pouvais faire:

Name.objects.filter(alias!="")

Alors, quel est l'équivalent de ce qui précède?

un33k
la source

Réponses:

841

Vous pouvez faire ceci:

Name.objects.exclude(alias__isnull=True)

Si vous devez exclure des valeurs nulles et des chaînes vides, la façon préférée de le faire est de chaîner les conditions comme ceci:

Name.objects.exclude(alias__isnull=True).exclude(alias__exact='')

Le chaînage de ces méthodes vérifie fondamentalement chaque condition indépendamment: dans l'exemple ci-dessus, nous excluons les lignes où aliasest nul ou une chaîne vide, de sorte que vous obtenez tous les Nameobjets qui ont un aliaschamp non nul, non vide . Le SQL généré ressemblerait à quelque chose comme:

SELECT * FROM Name WHERE alias IS NOT NULL AND alias != ""

Vous pouvez également passer plusieurs arguments à un seul appel à exclude, ce qui garantirait que seuls les objets répondant à toutes les conditions sont exclus:

Name.objects.exclude(some_field=True, other_field=True)

Ici, les lignes dans lesquelles some_field et other_field sont vraies sont exclues, nous obtenons donc toutes les lignes où les deux champs ne sont pas vrais. Le code SQL généré ressemblerait un peu à ceci:

SELECT * FROM Name WHERE NOT (some_field = TRUE AND other_field = TRUE)

Alternativement, si votre logique est plus complexe que cela, vous pouvez utiliser les objets Q de Django :

from django.db.models import Q
Name.objects.exclude(Q(alias__isnull=True) | Q(alias__exact=''))

Pour plus d'informations, consultez cette page et cette page dans la documentation Django.

En passant: Mes exemples SQL ne sont qu'une analogie - le code SQL généré sera probablement différent. Vous aurez une meilleure compréhension du fonctionnement des requêtes Django en regardant réellement le SQL qu'elles génèrent.

Sasha Chedygov
la source
5
Je crois que votre modification est erronée: le filtre de chaînage ne crée PAS automatiquement un SQL OR(uniquement dans ce cas), il produit un SQL AND. Voir cette page pour référence: docs.djangoproject.com/en/dev/topics/db/queries/… L'avantage du chaînage est que vous pouvez mélanger excludeet filtermodéliser des conditions de requête compliquées. Si vous souhaitez modéliser un vrai SQL, ORvous devez utiliser un objet Django Q: docs.djangoproject.com/en/dev/topics/db/queries/… Veuillez modifier votre modification pour refléter cela, car la réponse est gravement trompeuse en l'état .
shezi
1
@shezi: Je le pensais plus comme une analogie - je ne voulais pas dire que le code SQL réel est garanti d'utiliser un ORpour fusionner les conditions. Je vais modifier ma réponse pour clarifier.
Sasha Chedygov
1
Gardez à l'esprit qu'il existe différentes manières de représenter cette logique - par exemple, NOT (A AND B)est équivalent à NOT A OR NOT B. Je pense que cela rend les choses déroutantes pour les nouveaux développeurs de Django qui connaissent le SQL mais ne connaissent pas les ORM.
Sasha Chedygov
3
Je connais la loi de De Morgan, et c'est exactement ce que je veux dire: votre exemple ne fonctionne que parce qu'il profite de transformer le ANDdans la première requête en un ORparce que vous utilisez exclude. Dans le cas général, il est probablement plus correct de considérer le chaînage comme un THEN, c'est-à-dire exclude(A) THEN exclude(B). Désolé pour le langage dur ci-dessus. Votre réponse est vraiment bonne, mais je crains que les nouveaux développeurs ne prennent votre réponse trop généralement.
shezi
2
@shezi: Assez juste. Je suis d'accord qu'il vaut mieux y penser en termes Django et non pas en termes SQL, je pensais juste que présenter le chaînage en termes de ANDet ORpourrait être utile pour quelqu'un venant à Django depuis un arrière-plan SQL. Pour une compréhension plus approfondie de Django, je pense que les documents font un meilleur travail que moi.
Sasha Chedygov
49
Name.objects.filter(alias__gt='',alias__isnull=False)
jbofill
la source
2
Je ne suis pas sûr, mais je pense que la alias__isnull=Falsecondition est redondante. Si le champ l'est Nullsûrement, il sera exclu par la première clause?
Bobble
Mis à part mon commentaire / question précédente, je pense que la logique positive ici est plus facile à suivre que dans certaines des autres réponses.
Bobble
@Bobble qui dépendrait de l'implémentation de la base de données - la commande est déléguée à la base de données
wpercy
alias__gtétait la seule chose qui fonctionnait pour les colonnes de type JSON où je voulais exclure les chaînes vides de JSON comme {'something:''}. La syntaxe de travail est donc:jsoncolumnname__something__gt=''
bartgras
38

Premièrement, les documents Django recommandent fortement de ne pas utiliser de valeurs NULL pour les champs basés sur des chaînes tels que CharField ou TextField. Lisez la documentation pour l'explication:

https://docs.djangoproject.com/en/dev/ref/models/fields/#null

Solution: Je pense que vous pouvez également enchaîner des méthodes sur QuerySets. Essaye ça:

Name.objects.exclude(alias__isnull=True).exclude(alias="")

Cela devrait vous donner l'ensemble que vous recherchez.

b3ng0
la source
5

Depuis Django 1.8,

from django.db.models.functions import Length

Name.objects.annotate(alias_length=Length('alias')).filter(alias_length__gt=0)
Programmeur chimique
la source
5
Cela semble être «quelque chose que vous pouvez faire», pas quelque chose que vous devriez faire. Il épanouit considérablement la complexité des requêtes sur deux vérifications simples.
Oli
3

Pour éviter les erreurs courantes lors de l'utilisation exclude, n'oubliez pas:

Vous ne pouvez pas ajouter plusieurs conditions dans un bloc exclude () comme filter. Pour exclure plusieurs conditions, vous devez utiliser plusieurs exclude ()

Exemple

Incorrect :

User.objects.filter (email='[email protected] '). Exclude (profile__nick_name =' ', profile__avt =' ')

Correct :

User.objects.filter (email='[email protected] '). Exclude (profile__nick_name =' '). Exclude (profile__avt =' ')

HoangYell
la source
0

Vous pouvez simplement faire ceci:

Name.objects.exclude(alias="").exclude(alias=None)

C'est vraiment aussi simple que cela. filterest utilisé pour faire correspondre et excludepour faire correspondre tout sauf ce qu'il spécifie. Cela serait évalué en SQL comme NOT alias='' AND alias IS NOT NULL.

Tim Tisdall
la source
Ceci est une erreur. La question vise à exclure les alias empty ( alias="") et NULL ( alias=None) de la requête. Le vôtre inclurait des instances avec Name(alias=None).
damon
@damon - Je répondais à ce qui était l'équivalent .filter(alias!="")mais pas au titre. J'ai édité ma réponse. Cependant, les champs de caractères ne doivent pas autoriser les valeurs NULL et utiliser la chaîne vide pour une non-valeur (selon la convention).
Tim Tisdall
-1

c'est une autre façon simple de le faire.

Name.objects.exclude(alias=None)
ImadOS
la source
Nonen'est pas la même chose que "".
Tim Tisdall