2009-04-11 12 views
201

¿Cómo podría hacer un "o" en un filtro django?Django Filters - or?

Básicamente, quiero ser capaz de enumerar los elementos que ya sea un usuario ha agregado (que se enumeran como el creador) o el artículo ha sido aprobado

así que básicamente necesito seleccionar

item.creator = owner or item.moderated = False 

¿Cómo puedo hacer esto en django (preferiblemente con un filtro/queryset)

Respuesta

361

Hay Q objetos que permiten búsquedas complejas. Ejemplo:

from django.db.models import Q 

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

cómo ¿Podría hacerse esto programáticamente? Entonces, por ejemplo, puede tener filtros 'for f in: Item.objects.filter (Q (creador = f1) | Q (creador = f2) | ...)' – Alexis

+10

@AlexisK Use algo como 'reduce (lambda q , f: q | Q (creador = f), filtros, Q()) 'para crear el objeto Q grande. – Phob

+16

@alexis: también podría hacer 'Item.objects.filter (creator__in = creators)', por ejemplo. –

82

Puede usar la | operador para combinar QuerySets directamente sin necesidad de objetos Q:

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

(edit - Al principio estaba seguro si esto causó una consulta extra, pero @spookylukey señaló que la evaluación queryset perezosa se encarga de eso)

+4

Para saber qué consultas se ejecutan en una solicitud determinada, puede usar la barra de herramientas de depuración Aplicación Django. Está hecho de increíble y gana. –

+0

Estaba probando esto desde el caparazón. ¿Hay alguna forma de rastrear las consultas para la línea anterior directamente desde el shell? –

+21

haga 'desde la conexión de importación django.db' y use 'connection.queries'. Esto requiere DEBUG = True. Por cierto, debes saber que [QuerySets es flojo] (https://docs.djangoproject.com/en/dev/topics/db/queries/#querysets-are-lazy) y esto llega al DB solo una vez. – spookylukey

16

Quiere para hacer dinámico filtro entonces usted tiene que utilizar como Lambda

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]) | ..... 
+4

¡La respuesta perfecta para mí! Para python3, '' from functools import reduce' de antemano. – Dharmit

10

similares a answera mayor, 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 utilizando 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)) 

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

operator está en la biblioteca estándar: import operator
De docstring:

or_ (a, b) - Igual que a | segundo.

Para python3, reducir no está en la biblioteca estándar: from functools import reduce


P. S.

No olvide asegurarse de que list_of_Q no esté vacío - reduce() se ahogará en la lista vacía, necesita al menos un elemento.

9

Vale la pena tener en cuenta que es posible añadir Q expresiones.

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 = Users.objects.filter(query) 

Esto termina con una pregunta como:

(first_name = 'mark' or email = '[email protected]') and last_name = 'doe' 

De esta manera no hay necesidad de hacer frente a o operadores, reducir de etc.