Digital Ocean es la plataforma que uso para hostear la mayoría de mis proyectos, incluido este blog, por lo que puedo recomendártelo sinceramente, usar este enlace no generará un costo extra sobre ti, me ayuda a mantener bajos los costos del blog y tú obtendrás $200 USD en crédito, válidos por 2 meses (60 días)
Índice del contenido
Diferencias entre select_related y prefetch_related en Django
Diferencias entre select_related y prefetch_related en Django
Los métodos select_related y prefetch_related se usan para reducir el número de queries que se realizan a la base de datos. Lo anterior se traduce en tiempo de respuesta para cada vista. Además, usar estos métodos es una de las acciones a implementar para mejorar el rendimiento de una aplicación de Django.
Solo ten en mente que hay mejores cosas que optimizar en tu aplicación que obsesionarte con su rendimiento , pero sí insistes considera echarle un vistazo a aggregate y annotate, demás de tener cuidado con usar este último pues las subqueries pueden volver tus queries increíblemente lentas.
select_related
El método select_related se usa para seguir una relación de tipo ForeignKey o OneToOneField hacia los respectivos objetos a los que apunta y obtenerlos.
Al usar select_related tendremos una consulta más larga, sin embargo, la ventaja consiste en que ya no será necesario acceder nuevamente a la base de datos para obtener los objetos del modelo relacionado.
Esquema simplificado del funcionamiento de select_related
Considera este ejemplo:
from django.db import models
class Principal(models.Model):
name = models.CharField(max_length=256)
class Derivado(models.Model):
name = models.CharField(max_length=256)
principal = models.ForeignKey(
"Principal", related_name="derivados", on_delete=models.CASCADE
)
Si intentamos acceder al objeto al que apunta la relación Foreign Key, se generará una nueva consulta a la base de datos. select_related evita esa consulta extra por cada objeto.
{% for object in queryset %}
<p>{{object.name}}</p>
<small>{{object.principal.name}}</small>
{% endfor %}
Por ejemplo, si tenemos tres objetos Derivados relacionados a un único objeto principal:
- Una consulta principal que obtiene todos los objetos Derivado
- Tres consultas, exactamente iguales, una para cada vez que accedemos al objeto principal a partir del objeto Derivado.
Uso en una consulta
Para usar select_related lo llamamos a partir de nuestra consulta, pasándole el nombre del campo que corresponde a nuestra relación con el otro modelo.
Derivado.objects.select_related("principal")
Funcionamiento interno de select_related
¿Cómo funciona select_related internamente?, select_related reemplaza las consultas múltiples que se realizan por un único INNER JOIN a nivel de la base de datos:
SELECT "my_app_derivado"."id",
"my_app_derivado"."name",
"my_app_derivado"."principal_id"
FROM "my_app_derivado"
SELECT "my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
WHERE "my_app_principal"."id" = '1'
SELECT "my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
WHERE "my_app_principal"."id" = '1'
SELECT "my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
WHERE "my_app_principal"."id" = '1'
De esta manera se reducen las múltiples consultas SQL a una sola consulta más larga.
SELECT "my_app_derivado"."id",
"my_app_derivado"."name",
"my_app_derivado"."principal_id",
"my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_derivado"
INNER JOIN "my_app_principal"
ON ("my_app_derivado"."principal_id" = "my_app_principal"."id")
prefetch_related
Si el método select_related recupera un único objeto a partir de un campo de relación única, el método prefetch_related se usa cuando tenemos una relación múltiple con otro modelo, es decir, una relación de tipo ManyToMany o un ForeignKey inverso**.
Esquema simplificado del funcionamiento de prefetch_related
Considera este ejemplo, nota el campo ManyToManyField hacia el modelo Principal.
from django.db import models
class Principal(models.Model):
name = models.CharField(max_length=256)
class MultiplesPrincipales(models.Model):
name = models.CharField(max_length=256)
principales = models.ManyToManyField("Principal", related_name="multiples")
Si accedemos al campo que representa a la relación múltiple de nuestro objeto, sin usar prefetch_related, estaremos impactando la base de datos con una nueva consulta.
{% for object in queryset %}
<p>{{object.name}}</p>
{% for principal in object.principales.all %}
<!-- Una nueva consulta cada vez -->
<p><small>{{principal.name}}</small></p>
{% endfor %}
{% endfor %}
Uso en una consulta
Para usar el método prefetch_related llámalo al final de nuestra consulta, eligiendo aquel campo que represente la relación de muchos a muchos en nuestro objeto.
queryset = MultiplesPrincipales.objects.prefetch_related("principales")
Funcionamiento interno de prefetch_related
¿Cómo funciona internamente prefecth_related? El método prefetch_related reemplaza las múltiples consultas SQL por solo 2 consultas SQL: una para la query principal y la otra para los objetos relacionados, posteriormente, unirá los datos usando Python.
SELECT "my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
INNER JOIN "my_app_multiplesprincipales_principales"
ON ("my_app_principal"."id" = "my_app_multiplesprincipales_principales"."principal_id")
WHERE "my_app_multiplesprincipales_principales"."multiplesprincipales_id" = '1'
SELECT "my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
INNER JOIN "my_app_multiplesprincipales_principales"
ON ("my_app_principal"."id" = "my_app_multiplesprincipales_principales"."principal_id")
WHERE "my_app_multiplesprincipales_principales"."multiplesprincipales_id" = '2'
SELECT "my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
INNER JOIN "my_app_multiplesprincipales_principales"
ON ("my_app_principal"."id" = "my_app_multiplesprincipales_principales"."principal_id")
WHERE "my_app_multiplesprincipales_principales"."multiplesprincipales_id" = '3'
SELECT "my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
INNER JOIN "my_app_multiplesprincipales_principales"
ON ("my_app_principal"."id" = "my_app_multiplesprincipales_principales"."principal_id")
WHERE "my_app_multiplesprincipales_principales"."multiplesprincipales_id" = '4'
Las múltiples consultas anteriores quedan reducidas a solo 2 consultas SQL.
SELECT "my_app_multiplesprincipales"."id",
"my_app_multiplesprincipales"."name"
FROM "my_app_multiplesprincipales"
SELECT ("my_app_multiplesprincipales_principales"."multiplesprincipales_id") AS "_prefetch_related_val_multiplesprincipales_id",
"my_app_principal"."id",
"my_app_principal"."name"
FROM "my_app_principal"
INNER JOIN "my_app_multiplesprincipales_principales"
ON ("my_app_principal"."id" = "my_app_multiplesprincipales_principales"."principal_id")
WHERE "my_app_multiplesprincipales_principales"."multiplesprincipales_id" IN ('1', '2', '3', '4')