2012-03-12 17 views
7

Estoy tratando de mejorar un código existente que originalmente tomó 3 minutos para preparar una gran tabla de datos (luego devuelta por Ajax). El código anterior iteraba en un gran querySet, recopilando información de una variedad de objetos relacionados. Según lo que he leído, y al monitorear el registro de SQL, iterar sobre los conjuntos de consulta generalmente es una mala idea, porque SQL se ejecuta para cada elemento. En su lugar, he estado usando valores para recopilar información en una sola instrucción SQL, y luego recorrerla. Usando esta técnica, reduje el tiempo de ejecución a menos de 15 segundos (y aún no he terminado). Sin embargo, como ya no utilizo objetos modelo, no puedo usar get_FOO_display(). ¿Hay alguna manera de usar esta funcionalidad mientras usas valores()?Django: utilizando values ​​() y get_FOO_display()?

simplificado, el original era:

for user in users: 
    data.append(user.get_name_display()) # Appends 'Joe Smith' 
return data 

y el nuevo código es:

for user in users.values('name'): 
    data.append(user['name']) # Appends 'JSmith001', which is incorrect 
return data 

Además, si hay alguna otra manera de preservar la creación del modelo de objetos sin embargo, sólo se requiere una única SQL declaración en el backend, me encantaría saber al respecto. ¡Gracias!

+0

necesita ver más código. Esto es demasiado general para responder. –

+0

Disculpa, agregué los comentarios anteriores, pero no sé qué más quieres ver. – Nathan

Respuesta

5

En general, probablemente será mejor y más fácil utilizar una consulta basada en el administrador que devuelve objetos modelo. Parece que el problema con su enfoque original no es que estuviera iterando sobre su queryset (como dice @ahmoo, esto no es un problema de rendimiento), sino que dentro de su bucle de iteración estaba obteniendo objetos relacionados adicionales, que requerían uno o más consultas adicionales para cada registro.

Hay varias maneras de mejorar el rendimiento con consultas que todavía devuelven instancias de modelo:

  • Suena como el más relevante es select_related(), que va a hacer efectiva una unión de tablas en la consulta inicial para incluir datos de todos los objetos relacionados por claves externas.

  • Si eso no es suficiente, también puede agregar datos a las instancias de su modelo con extra(), lo que le permite pegar subconsultas en su SQL.

  • Y si todo eso falla, puede realizar raw SQL queries utilizando el método .raw() en una instancia de administrador, que aún devolverá las instancias del modelo.

Básicamente, si puede hacerlo en SQL de una manera que le da una fila por ejemplo, hay una manera de hacerlo en Django y obtener instancias de modelo posterior.

Para responder a su pregunta original, sin embargo, se puede obtener el nombre de visualización a través de la clase Field - es sólo feo:

def get_field_display(klass, field, value): 
    f = klass._meta.get_field(field) 
    return dict(f.flatchoices).get(value, value) 

# usage 
get_field_display(User, 'name', 'JSmith001') 
+0

Hm. Cuando hago 'User.objects.all()' luego iterar a través de eso haciendo algo como 'print user.name', solo ejecuta una sola instrucción SQL. Sin embargo, cuando 'imprimo user.team.name', ahora ejecuta una declaración de SQL para cada usuario, incluso si uso select_related o no. – Nathan

+0

¿'user.team' es una relación de clave externa, o una relación de muchos a muchos? 'select_related()' solo sigue a los campos 'ForeignKey', creo. – nrabinowitz

+0

Lo siento, es una relación de muchos a muchos. Correcto, lo leí y traté de prefetch_related brevemente antes de darme cuenta de que era un desarrollo solo actualmente. Creo que esta es la razón por la que terminé yendo con valores para rellenar mis datos en dicts, lo cual está funcionando bien (hasta 2,7 segundos con las correcciones finales), excepto que no puedo usar get_FOO_display. – Nathan

3

iteración sobre QuerySets es generalmente una mala idea, ya que SQL se ejecuta para cada elemento

eso no es verdad. A continuación se toma de the official docs:

Un QuerySet es iterable, y ejecuta su consulta la base de datos la primera vez que iterar sobre ella

creo que el problema tiene que ver con la definición de la users de tu codigo. ¿Qué le asignaste?

+0

También hacemos un montón de claves foráneas, lo que significa que pulsamos 'user.team.name'. Tiene razón en que solo ejecutan una única consulta sobre objetos simples, pero cuando intento depurar esto ahora, tan pronto como sigo una clave externa, hay una declaración SQL separada en el registro para cada usuario. – Nathan

+0

@Nathan - ¿Está tratando de acceder a 'user.team.name' o' user.name'? En su publicación original, parece que necesita 'user.name' pero luego mencionó' user.team.name'. Probablemente sea lo mejor si pudieras publicar la definición para el modelo 'Team' y expandir lo que deseas lograr. – tamakisquare

+0

Ambos, así como una gran cantidad de cosas diferentes de diferentes relaciones, que son una combinación de muchos a muchos y clave externa. Creo que ahora hay dos preguntas principales, 1) es posible obtener el nombre para mostrar de un modelo sin una instancia de ese objeto modelo, y 2) cómo puedo iterar mejor a través de un conjunto de consulta sin múltiples llamadas SQL. Estoy pensando que la respuesta al n. ° 1 es "no", y la respuesta al n. ° 2 es una publicación SO totalmente diferente o más introspección de código. – Nathan

Cuestiones relacionadas