Reduce tu tiempo de respuesta en Django: Querysets

django

Acabas de adquirir este framework llamado Django, jugaste un rato con su ORM y te encanta lo fácil que puedes usar. Sin embargo, notas que tarda años en cargar y al revisar tu  django-debug-toolbar descubres que tu vista tiene más de 100 querysets. ¿Qué hacer?

Intentaré mostrarle cómo evitar los problemas más comunes que pueden tener al hacer querysets y cómo reducir el recuento de estos. 

También te daré una breve explicación sobre cómo funcionan los querysets de Django.

Antes de iniciar, tenga en cuenta que es mejor leer la documentación para más detalles.

Digamos que nuestro nuevo proyecto tiene los siguientes modelos:

clase Persona(modelos.Modelo): 
    nombre = modelos.CharField(max_length=50) 
Teléfono de clase (modelos.Modelo): 
    número = modelos.CharField(max_length=50) 
    persona = modelos.ForeignKey(Persona)

Y ahora queremos enumerar a todos nuestros usuarios, por lo que tenemos en una de nuestras vistas, algo así:

Persona.objetos.todos()

 

Por supuesto, que la línea anterior le devolverá todas las instancias de Persona en la base de datos.

Esta consulta será rápida al principio, pero después de que comencemos a tener más instancias de Persona guardadas, llevará más tiempo.

Existe, entonces, una técnica para manejar esta situación conocida como paginación.

La paginación devuelve nuestras instancias de Persona en lotes, lo que reduce el tiempo que tarda en cargar la página. 

¿Ha estado en StackOverflow antes? Ve al final y verás la opción de cambiar a una página diferente.

Django tiene un objeto paginador integrado. Así que para paginar nuestras instancias de Persona todo lo que necesitamos hacer es lo siguiente:

personas = Persona.objetos.todos() paginador = Paginador(personas, 25) pagina = paginador.pagina(1)

Esta variable de página contendrá las primeras 25 instancias de Persona devueltas por nuestro queryset en el atributo object_list .

Necesitará más lógica para obtener una vista funcional, pero todo está incluido en la documentación.

Los recuentos, ¿qué hacer?

Ahora queremos mostrar un recuento del número de instancias de Phone que tiene cada persona.

Para ello usamos lo siguiente en nuestra plantilla:

 

{% por persona en personas %} 
    {{ persona.phone_set.count }} 
{% endfor%}

 

Si mantenemos nuestra plantilla como esta, generaremos una consulta adicional por Persona en nuestra consulta (La query COUNT), lo que significa mucha más carga para nuestros servidores.

Una de las ventajas será, entonces, que podemos recuperar todo en un número constante de consultas por usar prefetch_related.

 

En nuestro ejemplo, cargaremos todas las instancias de Phone para todas nuestras Personas:

personas = Person.objects.all().prefetch_related('phone_set')

 

Con lo anterior, hemos recuperado en 2 consultas lo que antes tomaba alrededor de N + 1, siendo N el número de instancias de Persona. También tenemos toda la instancia de Phone, lo cual es un desperdicio en este caso, porque solo queremos el COUNT. Sin embargo, reconocemos que este puede ser útil si necesita usar los otros campos.

Ahora bien, necesitamos actualizar la plantilla para evitar usar count, reemplazándola por la longitud que cuenta los objetos en Python. Podemos hacer lo siguiente, ya que hemos cargado todos los objetos:

{% por persona en personas %} 
    {{ persona.phone_set.all | longitud }} 
{% endfor%}

 

Recomendando leer más sobre prefetch_related y  select_related en la documentación. Esos métodos son esenciales cuando se quiere reducir el conteo de querysets.

 

El conteo 

Si lo único que desea es el conteo , puede utilizar las herramientas de agregación y anotación .

En lugar de recuperar las instancias completas de un queryset, puede indicar a Django que agregue los COUNT a sus resultados.

Puede hacer uso del . en lugar de recuperar las instancias completas de un queryset y puede indicar a Django que agregue los COUNT a sus resultados:

personas = Persona.objetos.anotar(phone_count=Count('phone'))

 

Ahora puede tener acceso al número de instancias de Phone:

{% por persona en personas %} 
    {{ persona.phone_count }} 
{% endfor%}

¿Hay algo más que pueda hacer para mejorar la velocidad de carga en mi aplicación?

 

Por supuesto, bienvenido al mundo del desarrollo de software (SIEMPRE hay algo más). Puede agregar caché en varios niveles de su aplicación, colas asíncronas para tareas largas, etc.

Si está interesado en estos temas, vaya a las páginas de documentación respectiva, ¡la documentación de Django es increíble!