¿Cómo hago un filtro OR en una consulta de Django?


303

Quiero poder enumerar los elementos que un usuario ha agregado (están listados como el creador) o que el elemento ha sido aprobado.

Entonces básicamente necesito seleccionar:

item.creator = owner or item.moderated = False

¿Cómo haría esto en Django? (preferiblemente con un filtro o conjunto de consultas).

Respuestas:


545

Hay Qobjetos que permiten búsquedas complejas. Ejemplo:

from django.db.models import Q

Item.objects.filter(Q(creator=owner) | Q(moderated=False))

66
¿Cómo podría hacerse esto programáticamente? Entonces, por ejemplo, poder tenerfor f in filters: Item.objects.filter(Q(creator=f1) | Q(creator=f2) | ...)
Alexis

14
@AlexisK Use algo como reduce(lambda q, f: q | Q(creator=f), filters, Q())crear el objeto Q grande.
Phob

24
@alexis: también podrías hacer Item.objects.filter(creator__in=creators), por ejemplo.
Kevin London

44
Si se pregunta (como yo) de dónde |proviene el uso como operador OR, en realidad es el operador de unión establecida. También se usa (no aquí) como bit a bit O: stackoverflow.com/questions/5988665/pipe-character-in-python
e100

124

Puedes usar el | operador para combinar conjuntos de consultas directamente sin necesidad de objetos Q:

result = Item.objects.filter(item.creator = owner) | Item.objects.filter(item.moderated = False)

(editar: inicialmente no estaba seguro de si esto causaba una consulta adicional, pero @spookylukey señaló que la evaluación de conjunto de consultas perezoso se encarga de eso)


44
Para averiguar qué consultas se ejecutan en una solicitud determinada, puede usar la aplicación Django de la barra de herramientas de depuración. Está hecho de increíble y gana.
Deniz Dogan

25
hacer 'desde django.db importar conexión' y usar 'connection.queries'. Esto requiere DEBUG = True. Por cierto, debes saber que QuerySets son vagos y esto golpea la base de datos solo una vez.
spookylukey

1
¿Podría excluirse ser utilizado con comparaciones negadas?
Neob91

2
¿Esto puede dar como resultado duplicados en el conjunto de consultas de resultados?
Charles Haro

1
Más específicamente, los conjuntos de consultas tienden a golpear la base de datos solo cuando intenta indexarlos, de lo contrario, solo está creando una consulta.
awiebe

41

Vale la pena señalar que es posible agregar expresiones Q.

Por ejemplo:

from django.db.models import Q

query = Q(first_name='mark')
query.add(Q(email='mark@test.com'), Q.OR)
query.add(Q(last_name='doe'), Q.AND)

queryset = User.objects.filter(query)

Esto termina con una consulta como:

(first_name = 'mark' or email = 'mark@test.com') and last_name = 'doe'

De esta manera no hay necesidad de tratar o operadores, reducir de etc.


2
¿Pero es más fácil de escribir query |= Q(email='mark@test.com')?
Alex78191

26

Si desea hacer que el filtro sea dinámico, debe usar Lambda como

from django.db.models import Q

brands = ['ABC','DEF' , 'GHI']

queryset = Product.objects.filter(reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]))

reduce(lambda x, y: x | y, [Q(brand=item) for item in brands]) es equivalente a

Q(brand=brands[0]) | Q(brand=brands[1]) | Q(brand=brands[2]) | .....

66
¡La respuesta perfecta para mí! Para python3, hazlo de from functools import reduceantemano.
Dharmit

1
¿Por qué no usar en operator.or_lugar de lambda x, y: x | y?
Alex78191

20

Similar a la answera anterior, pero un poco más simple, sin la lambda:

filter_kwargs = {
    'field_a': 123,
    'field_b__in': (3, 4, 5, ),
}

Para filtrar estas dos condiciones usando OR:

Item.objects.filter(Q(field_a=123) | Q(field_b__in=(3, 4, 5, ))

Para obtener el mismo resultado mediante programación:

list_of_Q = [Q(**{key: val}) for key, val in filter_kwargs.items()]
Item.objects.filter(reduce(operator.or_, list_of_Q))

(dividido en dos líneas aquí, para mayor claridad)

operatorestá en la biblioteca estándar: import operator
desde docstring:

o_ (a, b): igual que a | si.

Para Python3, reduceya no está integrado, pero todavía está en la biblioteca estándar:from functools import reduce


PD

No olvides asegurarte de list_of_Qque no esté vacío: reduce()se ahogará en la lista vacía, necesita al menos un elemento.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.