Acabas de adquirir este framework llamado Django, jugaste un rato con su ORM y te encanta lo fácil que puedes trabajar con él.

Empiezas a notar que tu página perfecta tarda años en cargarse. Revisas tu  django-debug-toolbar y descubres que tu vista tiene más de 100 queries!!!

Esta situación me ha pasado antes. Intentaré mostrarle cómo evitar los problemas más comunes que puede tener al hacer queries y cómo reducir el recuento de querysets en general. También daré una breve explicación sobre cómo funcionan los querysets de Django. Tenga en cuenta que es mejor leer la documentación para detalles.

Digamos que nuestro nuevo brillante proyecto tiene los siguientes modelos:

class Person(models.Model):
    name = models.CharField(max_length=50)
class Phone(models.Model):
    number = models.CharField(max_length=50)
    person = models.ForeignKey(Person)

¿Dónde están mis páginas?

Queremos enumerar a todos nuestros usuarios, por lo que tenemos algo como esto en una de nuestras vistas:

Person.objects.all()

Esto, por supuesto, devolverá todas las instancias de Persona en nuestra base de datos. Esta consulta será rápida al principio, pero después de que comencemos a tener más y más instancias de Persona guardadas en nuestra base de datos, esto llevará más y más tiempo. Existe una técnica conocida para manejar esto llamada paginación. Esto devuelve nuestras instancias de Persona por lotes en lugar de devolver todo, reduciendo el tiempo que la página tardará en cargar. ¿Has 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. Para paginar nuestras instancias de Persona todo lo que necesitamos es lo siguiente:

people = Person.objects.all() paginator = Paginator(people, 25) page = paginator.page(1)

LEER
Cómo crear un módulo personalizado para Beaver Builder

Esta variable de página contendrá las primeras 25 instancias de Persona retornadas por nuestro queryset en el atributo object_list. Necesitará más lógica para obtener una vista funcional, pero todo está incluído en la documentación..

¡Apesto en los recuentos!

Ahora queremos mostrar un recuento del número de instancias de Phone que tiene cada persona. Para esto usamos algo como así en nuestra plantilla:

{% for person in persons %}
    {{ person.phone_set.count }}
{% endfor %}

Si mantenemos nuestra plantilla como está ahora, generaremos una query adicional por Persona en nuestra query (La query COUNT), lo que significa mucha más carga para nuestros servidores. Afortunadamente, podemos recuperar todo en un número constante de queries por  usando prefetch_related. En nuestro ejemplo, cargaremos todas las instancias de Phone para todas nuestras Personas:

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

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

Necesitamos actualizar la plantilla para evitar usar count, reemplazándola por la longitud que cuenta los objetos en Python. Podemos hacer esto porque ya hemos cargado todos los objetos:

{% for person in persons %}
    {{ person.phone_set.all | length }}
{% endfor %}

Recomiendo 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.

¿Qué si solo quiero el CONTEO?

En ese caso, 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:

LEER
Diseño de páginas web para odontólogos y médicos especialistas

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

persons = Person.objects.annotate(phone_count=Count('phone'))

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

{% for person in persons %}
    {{ person.phone_count }}
{% endfor %}

¿Hay algo más que pueda hacer para mejorar la velocidad de carga de 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. Hay demasiados temas para cubrir en esta breve publicación.

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


Comentarios