Filtrado de nombres vacíos o NULL en un conjunto de consultas

463

Tengo first_name, last_namey alias(opcional), que tengo que buscar. Entonces, necesito una consulta para darme todos los nombres que tienen un conjunto de alias.

Solo si pudiera hacer:

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

Entonces, ¿cuál es el equivalente a lo anterior?

un33k
fuente

Respuestas:

841

Podrías hacer esto:

Name.objects.exclude(alias__isnull=True)

Si necesita excluir valores nulos y cadenas vacías, la forma preferida de hacerlo es encadenar las condiciones de la siguiente manera:

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

El encadenar estos métodos básicamente verifica cada condición de forma independiente: en el ejemplo anterior, excluimos las filas donde aliases nulo o una cadena vacía, por lo que obtiene todos los Nameobjetos que tienen un campo no nulo, no vacío alias. El SQL generado se vería así:

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

También puede pasar múltiples argumentos a una sola llamada a exclude, lo que garantizaría que solo los objetos que cumplan con todas las condiciones sean excluidos:

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

Aquí, las filas en las que some_field y other_field son verdaderas se excluyen, por lo que obtenemos todas las filas donde ambos campos no son verdaderos. El código SQL generado se vería un poco así:

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

Alternativamente, si su lógica es más compleja que eso, podría usar los objetos Q de Django :

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

Para obtener más información, consulte esta página y esta página en los documentos de Django.

Por otro lado: Mis ejemplos de SQL son solo una analogía: el código SQL generado probablemente se verá diferente. Obtendrá una comprensión más profunda de cómo funcionan las consultas de Django al observar realmente el SQL que generan.

Sasha Chedygov
fuente
55
Creo que su edición es incorrecta: el filtro de encadenamiento NO crea automáticamente un SQL OR(solo en este caso), produce un SQL AND. Consulte esta página como referencia: docs.djangoproject.com/en/dev/topics/db/queries/… La ventaja del encadenamiento es que puede mezclar excludey filtermodelar condiciones de consulta complicadas. Si desea modelar un SQL real OR, debe usar un objeto Django Q: docs.djangoproject.com/en/dev/topics/db/queries/… Edite su edición para reflejar esto, ya que la respuesta es muy engañosa tal como está. .
shezi
1
@shezi: Lo dije más como una analogía: no quise decir que el código SQL real esté garantizado para usar un ORpara fusionar las condiciones. Editaré mi respuesta para aclarar.
Sasha Chedygov
1
Tenga en cuenta que existen diferentes formas de representar esta lógica, por ejemplo, NOT (A AND B)es equivalente a NOT A OR NOT B. Creo que eso confunde las cosas a los nuevos desarrolladores de Django que conocen SQL pero no están familiarizados con los ORM.
Sasha Chedygov
3
Conozco la ley de De Morgan, y este es exactamente mi punto: su ejemplo solo funciona porque se aprovecha para convertir ANDla primera consulta en una ORporque está utilizando exclude. En el caso general, probablemente sea más correcto pensar en encadenar como a THEN, es decir exclude(A) THEN exclude(B). Perdón por el lenguaje duro anterior. Su respuesta es realmente buena, pero me preocupa que los nuevos desarrolladores también tomen su respuesta en general.
shezi
2
@ Shezi: Bastante justo. Estoy de acuerdo en que es mejor pensarlo en términos de Django y no en términos de SQL, solo pensé que presentar encadenamiento en términos de ANDy ORpodría ser útil para alguien que viene a Django desde un fondo SQL. Para una comprensión más profunda de Django, creo que los documentos hacen un mejor trabajo que yo.
Sasha Chedygov
49
Name.objects.filter(alias__gt='',alias__isnull=False)
jbofill
fuente
2
No estoy seguro, pero creo que la alias__isnull=Falsecondición es redundante. Si el campo es Nullseguro, ¿será excluido por la primera cláusula?
Bobble
Aparte de mi comentario / pregunta anterior, creo que la lógica positiva aquí es más fácil de seguir que en algunas de las otras respuestas.
Bobble
@Bobble que dependería de la implementación de la base de datos - el pedido se delega a la base de datos
wpercy
alias__gtfue lo único que funcionó para las columnas de tipo JSON donde quería excluir cadenas vacías de JSON como {'something:''}. Entonces la sintaxis de trabajo es:jsoncolumnname__something__gt=''
bartgras
38

En primer lugar, los documentos de Django recomiendan no usar valores NULL para campos basados ​​en cadenas como CharField o TextField. Lea la documentación para la explicación:

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

Solución: creo que también puedes encadenar métodos en QuerySets. Prueba esto:

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

Eso debería darte el set que estás buscando.

b3ng0
fuente
5

Desde Django 1.8,

from django.db.models.functions import Length

Name.objects.annotate(alias_length=Length('alias')).filter(alias_length__gt=0)
Programador químico
fuente
55
Esto parece un "algo que puedes hacer", no algo que debes hacer. Florece significativamente la complejidad de la consulta en dos verificaciones simples.
Oli
3

Para evitar errores comunes al usar exclude, recuerde:

Puede no añadir varias condiciones en una excluir () bloque como filter. Para excluir múltiples condiciones, debe usar la exclusión múltiple ()

Ejemplo

Incorrecto :

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

Correcto :

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

HoangYell
fuente
0

Simplemente puedes hacer esto:

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

Realmente es así de simple. filterse usa para hacer coincidir y excludepara hacer coincidir todo menos lo que especifica. Esto se evaluaría en SQL como NOT alias='' AND alias IS NOT NULL.

Tim Tisdall
fuente
Esto es incorrecto. La pregunta tiene como objetivo excluir los alias empty ( alias="") y NULL ( alias=None) de la consulta. El tuyo incluiría instancias con Name(alias=None).
damon
@damon: estaba respondiendo a lo que era equivalente .filter(alias!="")pero no al título. He editado mi respuesta. Sin embargo, los campos de caracteres no deben permitir valores NULL y usar la cadena vacía para un valor no (según la convención).
Tim Tisdall
-1

Esta es otra forma simple de hacerlo.

Name.objects.exclude(alias=None)
ImadOS
fuente
NoneNo es lo mismo que "".
Tim Tisdall el