Para optimizar el tiempo de iteración de tus listas en Django hay varias herramientas que te permiten llegar a tu objetivo. Hay toolbars de debug y varias funciones.

Hoy te enseñaremos a usar la función prefetch_related para reducir la cantidad de queries y su tiempo de ejecución para búsquedas en atributos de relaciones.

Decorador para debugger

Utilizaremos este decorator para contar cuántas queries son realizadas y los segundos que tardan en ejecutarse

from django.db import connection, reset_queries
import time
import functools
 
def query_debugger(func):
 
   @functools.wraps(func)
   def inner_func(*args, **kwargs):
 
       reset_queries()
 
       start_queries = len(connection.queries)
 
       start = time.perf_counter()
       result = func(*args, **kwargs)
       end = time.perf_counter()
 
       end_queries = len(connection.queries)
 
       print(f"Function : {func.__name__}")
       print(f"Number of Queries : {end_queries - start_queries}")
       print(f"Finished in : {(end - start):.2f}s")
       return result
 
   return inner_func

Sin prefetch_related esa función hace 1 query para obtener los posts y por las relaciones hace 3 queries para obtener el name:

  • Una para la relación con Signature
  • Otra para la relación con Author
  • La relación con Country

Por ende, hace 1 + 3 * cantidad_de_posts queries.

@query_debugger
def test_no_prefetch():
   posts = Post.objects.filter(signature__author__country__name__in=['Argentina', 'Uruguay'])
 
   for post in posts:
       name = post.signature.author.country.name
       print(name)

Con prefetch_related solo hace en total 4 queries:

1 - Una para los Posts 2 - Una para los Signatures de esos Posts 3 - Una para los Authors de esas Signatures 4 - Una para Countries de esos Authors

@query_debugger
def test_prefetch():
   posts = Post.objects.filter(signature__author__country__name__in=['Argentina', 'Uruguay'])
   posts = posts.prefetch_related('signature__author__country')
 
   for post in posts:
       name = post.signature.author.country.name
       print(name)