¿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).

Mez
fuente

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))
Alex Koshelev
fuente
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)

Andy Baker
fuente
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='[email protected]'), 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 = '[email protected]') and last_name = 'doe'

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

marxin
fuente
2
¿Pero es más fácil de escribir query |= Q(email='[email protected]')?
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]) | .....
Abhishek Chauhan
fuente
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.

frnhr
fuente