2008-11-22 16 views
121

dado una clase:En Django, ¿cómo se filtra un QuerySet con búsquedas de campo dinámicas?

from django.db import models 

class Person(models.Model): 
    name = models.CharField(max_length=20) 

¿Es posible, y si es así cómo, a tener un QuerySet que los filtros basados ​​en argumentos dinámicos? Por ejemplo:

# Instead of: 
Person.objects.filter(name__startswith='B') 
# ... and: 
Person.objects.filter(name__endswith='B') 

# ... is there some way, given: 
filter_by = '{0}__{1}'.format('name', 'startswith') 
filter_value = 'B' 

# ... that you can run the equivalent of this? 
Person.objects.filter(filter_by=filter_value) 
# ... which will throw an exception, since `filter_by` is not 
# an attribute of `Person`. 

Respuesta

222

expansión argumento de Python se puede utilizar para resolver este problema:

kwargs = { 
    '{0}__{1}'.format('name', 'startswith'): 'A', 
    '{0}__{1}'.format('name', 'endswith'): 'Z' 
} 

Person.objects.filter(**kwargs) 

Este es un lenguaje Python muy común y útil.

+5

Solo un aviso rápido de getcha: asegúrese de que las cadenas en los kwargs sean de tipo str no unicode, sino que filter() se quejará. –

+0

¿Lo harás? ¿No coacciona en este punto? – jMyles

+0

Gracias Daniel! Me ayudó. ¿Cómo se llama en Python? ¿Argumento de expansión? No puedo encontrarlo en los documentos. – santiagobasulto

-1

un muy complejas formas de búsqueda por lo general indica que un modelo más simple está tratando de cavar su salida.

¿Cómo espera exactamente obtener los valores para el nombre y el funcionamiento de la columna? ¿Dónde obtienes los valores de 'name' y 'startswith'?

filter_by = '%s__%s' % ('name', 'startswith') 
  1. Una forma de "búsqueda"? Vas a ... ¿Qué? - elegir el nombre de una lista de nombres? Elija la operación de una lista de operaciones? Si bien son abiertos, la mayoría de las personas lo encuentran confuso y difícil de usar.

    ¿Cuántas columnas tienen tales filtros? 6? 12? 18?

    • ¿Unas pocas? Una lista de selección compleja no tiene sentido. Algunos campos y algunas declaraciones if tienen sentido.
    • ¿Un número grande? Tu modelo no suena bien. Parece que el "campo" es en realidad la clave de una fila en otra tabla, no en una columna.
  2. Botones de filtro específicos. Espera ... Así es como funciona el administrador de Django. Los filtros específicos se convierten en botones. Y el mismo análisis que el anterior se aplica. Algunos filtros tienen sentido. Una gran cantidad de filtros generalmente significa una especie de primera violación de forma normal.

Una gran cantidad de campos similares a menudo significa que debería haber habido más filas y menos campos.

+0

Gracias por la respuesta. El modelo de datos dado como ejemplo es solo eso, para una fácil ilustración. Prefiero no imaginar a nadie poniendo algo tan atroz en un proyecto real. ;) Quiero desacoplar relaciones genéricas y factorizar alguna lógica reutilizable. –

+0

Django ya * es * genérico. Escribir cosas más genéricas sobre Django es demasiado genérico. Mi recomendación es simplemente implementar su aplicación, evitando la generalización excesiva de un marco ya genérico. –

+8

Con respecto, es presuntuoso hacer recomendaciones sin saber nada sobre el diseño. Para "simplemente implementar" esta aplicación tendría funciones astronómicas (> 200 aplicaciones^21 foos) para cumplir con los requisitos. Estás leyendo propósito e intención en el ejemplo; no deberias :) –

6

Un ejemplo simplificado:

En una aplicación de encuesta de Django, quería un archivo HTML lista de selección muestra a los usuarios registrados. Pero debido a que tenemos 5000 usuarios registrados, necesitaba una forma de filtrar esa lista en función de los criterios de consulta (como solo las personas que completaron un determinado taller). Para que el elemento de la encuesta sea reutilizable, necesitaba que la persona que crea la pregunta de la encuesta pueda adjuntar esos criterios a esa pregunta (no quiero codificar la consulta en la aplicación).

La solución que se me ocurrió no es 100% fácil de usar (requiere la ayuda de un técnico para crear la consulta) pero resuelve el problema. Al crear la pregunta, el editor puede entrar en un diccionario en un campo personalizado, por ejemplo .:

{'is_staff':True,'last_name__startswith':'A',} 

Esa cadena se almacena en la base de datos. En el código de vista, regresa como self.question.custom_query. El valor de eso es una cadena que parece como un diccionario.Nos dirigimos de nuevo en un diccionario verdadera con eval() y luego meterlo en el conjunto de consultas con kwargs **:

kwargs = eval(self.question.custom_query) 
user_list = User.objects.filter(**kwargs).order_by("last_name") 
+0

Me pregunto qué se necesitaría para crear un ModelField/FormField/WidgetField personalizado que implementara el comportamiento para permitir al usuario, en el lado de la GUI, básicamente "construir" una consulta, nunca ver el texto real, pero usando una interfaz para hacerlo Suena como un proyecto ordenado ... –

+1

T. Stone - Me imagino que sería fácil construir una herramienta de este tipo de una manera simplista si los modelos que necesitan consulta fueran simples, pero muy difíciles de hacer de manera exhaustiva que expusieran todas las opciones posibles, especialmente si los modelos son complejos. – shacker

+2

-1 llamar a 'eval()' en la importación del usuario es una mala idea, incluso si confía completamente en sus usuarios. Un campo JSON sería una mejor idea aquí. –

6

Django.db.models.Q es exactamente lo que quiere de una manera Django.

+5

¿Podría (o alguien) proporcionar un ejemplo de cómo usar objetos Q al usar nombres de campos dinámicos? – jackdbernier

+3

Es lo mismo que en [respuesta de Daniel Naab] (http://stackoverflow.com/a/310785/4021086) La única diferencia es que pasa los argumentos al constructor del objeto Q. 'Q (** filters)', si desea construir dinámicamente objetos Q puede ponerlos en una lista y usar '.filter (* q_objects)', o usar los operadores bit a bit para combinar los objetos Q. –

Cuestiones relacionadas