2009-05-12 20 views
68

De un ejemplo se puede ver un múltiplo o consulta de filtro:¿Cómo redactar dinámicamente un filtro de consulta O en Django?

Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3)) 

Por ejemplo, esto se traduce en:

[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>] 

Sin embargo, quiero crear este filtro de consulta de una lista. ¿Como hacer eso?

p. Ej. [1, 2, 3] -> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))

+1

Parece que lo ha preguntado dos veces: http://stackoverflow.com/questions/852404/ –

+0

Para este caso de uso específico, probablemente use 'Article.objects .filter (pk__in = [1, 2, 3]) 'en django moderno, pero la pregunta sigue siendo relevante si quieres hacer algo un poco más avanzado al ordenar objetos Q juntos. – beruic

Respuesta

107

Usted podría cadena de consultas de la siguiente manera:

values = [1,2,3] 

# Turn list of values into list of Q objects 
queries = [Q(pk=value) for value in values] 

# Take one Q object from the list 
query = queries.pop() 

# Or the Q object with the ones remaining in the list 
for item in queries: 
    query |= item 

# Query the model 
Article.objects.filter(query) 
+2

¡Gracias! Esto era lo que estaba buscando :) No sabía que podía hacer | = –

+14

También podía inicializar la consulta usando: query = Q() – chachan

+3

Puede hacer campos dinámicos usando \ * \ * {'fieldname': value }: ** queries = [Q (\ * \ * {'fieldname': value}) para el valor en valores] ** – rechie

7

Véase el docs:

>>> Blog.objects.in_bulk([1]) 
{1: <Blog: Beatles Blog>} 
>>> Blog.objects.in_bulk([1, 2]) 
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>} 
>>> Blog.objects.in_bulk([]) 
{} 

Tenga en cuenta que este método sólo funciona para las búsquedas de clave principal, pero que parece ser lo que estás tratando de hacer.

Así que lo que quiere decir:

Article.objects.in_bulk([1, 2, 3]) 
4

Puede utilizar el | = operador para actualizar mediante programación una consulta utilizando objetos Q.

+1

¿Está documentado esto en algún lugar? He estado buscando los últimos 15 minutos, y esto es lo único que puedo encontrar. –

+0

¡Como mucho en nuestra industria, está documentado en StackOverflow! – Chris

18

Tal vez sea mejor usar la instrucción sql IN.

Article.objects.filter(id__in=[1, 2, 3]) 

Ver queryset api reference.

Si realmente necesita para hacer consultas con lógica dinámica, se puede hacer algo como esto (fea + no probado):

query = Q(field=1) 
for cond in (2, 3): 
    query = query | Q(field=cond) 
Article.objects.filter(query) 
+1

También podría usar 'query | = Q (field = cond)' – Bobort

39

Una forma más corta de escribir la respuesta de Dave Webb usando python's reduce function:

# For Python 3 only 
from functools import reduce 

values = [1,2,3] 

# Turn list of values into one big Q objects 
query = reduce(lambda q,value: q|Q(pk=value), values, Q()) 

# Query the model 
Article.objects.filter(query) 
+0

Parece que la reducción "incorporada" fue eliminada y reemplazada por 'functools.reduce'. [fuente] (https://docs.python.org/3.0/whatsnew/3.0.html#builtins) – lsowen

+0

Gracias @lsowen, corregido. –

24
from functools import reduce 
from operator import or_ 
from django.db.models import Q 

values = [1, 2, 3] 
query = reduce(or_, (Q(pk=x) for x in values)) 
+0

Bien, pero ¿de dónde viene el 'operador'? – mpiskore

+0

@mpiskore: Mismo lugar que cualquier otro módulo de Python: lo importa. –

+0

divertido. esa era realmente mi pregunta: ¿en qué módulo/biblioteca puedo encontrarla? Google no ayudó mucho. – mpiskore

5

En caso de querer establecer mediante programación qué campo db queremos ver los resultados:

import operator 
questions = [('question__contains', 'test'), ('question__gt', 23)] 
q_list = [Q(x) for x in questions] 
Poll.objects.filter(reduce(operator.or_, q_list)) 
1

Esta es para la lista pk dinámica:

pk_list = qs.values_list('pk', flat=True) # i.e [] or [1, 2, 3] 

if len(pk_list) == 0: 
    Article.objects.none() 

else: 
    q = None 
    for pk in pk_list: 
     if q is None: 
      q = Q(pk=pk) 
     else: 
      q = q | Q(pk=pk) 

    Article.objects.filter(q) 
+0

Puede usar 'q = Q()' en lugar de 'q = None', luego eliminar la cláusula' if q is None' - un poco menos eficiente pero puede eliminar tres líneas de código. (La Q vacía se fusiona posteriormente cuando se ejecuta la consulta.) – Chris

48

para crear consultas más complejas también existe la opción a utilizar construido en la Q() constantes del objeto Q.OR y Q.AND junto con el método add(), así:

list = [1, 2, 3] 
# it gets a bit more complicated if we want to dynamically build 
# OR queries with dynamic/unknown db field keys, let's say with a list 
# of db fields that can change like the following 
# list_with_strings = ['dbfield1', 'dbfield2', 'dbfield3'] 

# init our q objects variable to use .add() on it 
q_objects = Q() 

# loop trough the list and create an OR condition for each item 
for item in list: 
    q_objects.add(Q(pk=item), Q.OR) 
    # for our list_with_strings we can do the following 
    # q_objects.add(Q(**{item: 1}), Q.OR) 

queryset = Article.objects.filter(q_objects) 

# sometimes the following is helpful for debugging (returns the SQL statement) 
# print queryset.query 
+6

Para los recién llegados a este hilo, como yo, creo que esta respuesta debe considerarse como la mejor respuesta. Es más Djangoque que la respuesta aceptada. ¡Gracias! – theresaanna

+3

Discutiría que es más pitónico usar los operadores integrados OR y AND (| y &). 'q_objects | = Q (pk = item)' – Bobort

+0

¡Perfecto! ¡Gracias! –

0

fácil ..
de importación de django.db.models Q importe su modelo args = (Q (visibility = 1) | (Q (visibility = 0) & Q (user = self.de usuario))) #Tuple parámetros = {} #dic orden = 'create_at' límite = 10

Models.objects.filter(*args,**parameters).order_by(order)[:limit] 
1

Otra opción que no era consciente de hasta hace poco - QuerySet anula también el &, |, ~ , etc., operadores. Las otras respuestas que los objetos o Q son una mejor solución a esta pregunta, pero por el bien de interés/argumento, que puede hacer:

id_list = [1, 2, 3] 
q = Article.objects.filter(pk=id_list[0]) 
for i in id_list[1:]: 
    q |= Article.objects.filter(pk=i) 

str(q.query) volverán una consulta con todos los filtros de la cláusula WHERE.

1

Usando una solución con reduce y or_ operador aquí, ¿cómo se puede filtrar por campos de multiplicación?

from functools import reduce 
from operator import or_ 
from django.db.models import Q 

filters = {'field1': [1, 2], 'field2': ['value', 'other_value']} 

qs = Article.objects.filter(
    reduce(or_, (
     Q(**{f'{field}__in': list_filters[field]}) 
     for field in list_filters 
     ) 
    ) 
) 

p.s. f es un nuevo formato de cadenas literal aded en python3.6

Cuestiones relacionadas