Saltar al contenido

¿Cuál es la diferencia entre select_related y prefetch_related en Django ORM?

Solución:

Tu comprensión es mayormente correcta. Tu usas select_related cuando el objeto que va a seleccionar es un solo objeto, entonces OneToOneField o un ForeignKey. Tu usas prefetch_related cuando vas a obtener un “conjunto” de cosas, así que ManyToManyFields como dijiste o al revés ForeignKeys. Solo para aclarar lo que quiero decir con “revertir ForeignKeys “aquí hay un ejemplo:

class ModelA(models.Model):
    pass

class ModelB(models.Model):
    a = ForeignKey(ModelA)

ModelB.objects.select_related('a').all() # Forward ForeignKey relationship
ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship

La diferencia es que select_related realiza una unión SQL y, por lo tanto, obtiene los resultados como parte de la tabla del servidor SQL. prefetch_related por otro lado ejecuta otra consulta y por lo tanto reduce las columnas redundantes en el objeto original (ModelA en el ejemplo anterior). Puedes utilizar prefetch_related para cualquier cosa que puedas usar select_related por.

Las compensaciones son que prefetch_related tiene que crear y enviar una lista de ID para seleccionar de nuevo al servidor, esto puede llevar un tiempo. No estoy seguro de si hay una buena manera de hacer esto en una transacción, pero tengo entendido que Django siempre envía una lista y dice SELECT … WHERE pk IN (…, …, …) básicamente. En este caso, si los datos precargados son escasos (digamos objetos del estado de EE. UU. Vinculados a las direcciones de las personas), esto puede ser muy bueno; sin embargo, si está más cerca de uno a uno, esto puede desperdiciar muchas comunicaciones. En caso de duda, pruebe ambos y vea cuál funciona mejor.

Todo lo comentado anteriormente es básicamente sobre las comunicaciones con la base de datos. Sin embargo, en el lado de Python prefetch_related tiene el beneficio adicional de que se utiliza un solo objeto para representar cada objeto en la base de datos. Con select_related Se crearán objetos duplicados en Python para cada objeto “principal”. Dado que los objetos en Python tienen una sobrecarga de memoria decente, esto también puede ser una consideración.

Ambos métodos logran el mismo propósito, prescindir de consultas de base de datos innecesarias. Pero utilizan diferentes enfoques para la eficiencia.

La única razón para usar cualquiera de estos métodos es cuando una sola consulta grande es preferible a muchas consultas pequeñas. Django usa la consulta grande para crear modelos en la memoria de forma preventiva en lugar de realizar consultas bajo demanda en la base de datos.

select_related realiza una combinación con cada búsqueda, pero extiende la selección para incluir las columnas de todas las tablas unidas. Sin embargo, este enfoque tiene una salvedad.

Las uniones tienen el potencial de multiplicar el número de filas en una consulta. Cuando realiza una combinación sobre una clave externa o un campo uno a uno, el número de filas no aumentará. Sin embargo, las uniones de varios a varios no tienen esta garantía. Entonces, Django restringe select_related a relaciones que no resultarán inesperadamente en una unión masiva.

los “únete a Python” por prefetch_related es un poco más alarmante de lo que debería ser. Crea una consulta independiente para cada tabla que se va a unir. Filtra cada una de estas tablas con una cláusula WHERE IN, como:

SELECT "credential"."id",
       "credential"."uuid",
       "credential"."identity_id"
FROM   "credential"
WHERE  "credential"."identity_id" IN
    (84706, 48746, 871441, 84713, 76492, 84621, 51472);

En lugar de realizar una única combinación con potencialmente demasiadas filas, cada tabla se divide en una consulta separada.

Revisó las respuestas ya publicadas. Solo pensé que sería mejor si agregaba una respuesta con un ejemplo real.

Supongamos que tiene 3 modelos de Django que están relacionados.

class M1(models.Model):
    name = models.CharField(max_length=10)

class M2(models.Model):
    name = models.CharField(max_length=10)
    select_relation = models.ForeignKey(M1, on_delete=models.CASCADE)
    prefetch_relation = models.ManyToManyField(to='M3')

class M3(models.Model):
    name = models.CharField(max_length=10)

Aquí puedes consultar M2 modelo y su relativo M1 objetos usando select_relation campo y M3 objetos usando prefetch_relation campo.

Sin embargo, como hemos mencionado M1la relación de M2 es un ForeignKey, solo regresa 1 registro para cualquier M2 objeto. Lo mismo se aplica para OneToOneField así como.

Pero M3la relación de M2 es un ManyToManyField que puede devolver cualquier número de M1 objetos.

Considere un caso en el que tiene 2 M2 objetos m21, m22 que tienen lo mismo 5 asociado M3 objetos con ID 1,2,3,4,5. Cuando recuperas asociado M3 objetos para cada uno de esos M2 objetos, si usa seleccionar relacionado, así es como va a funcionar.

Pasos:

  1. Encontrar m21 objeto.
  2. Consultar todos los M3 objetos relacionados con m21 objeto cuyos ID son 1,2,3,4,5.
  3. Repita lo mismo para m22 objeto y todos los demás M2 objetos.

Como tenemos lo mismo 1,2,3,4,5 ID para ambos m21, m22 objetos, si usamos la opción select_related, va a consultar la base de datos dos veces para los mismos ID que ya se obtuvieron.

En cambio, si usa prefetch_related, cuando intente obtener M2 objetos, tomará nota de todos los ID que devolvieron sus objetos (Nota: solo los ID) al realizar la consulta M2 tabla y como último paso, Django va a hacer una consulta a M3 tabla con el conjunto de todos los ID que su M2 los objetos han regresado. y únete a ellos para M2 objetos usando Python en lugar de la base de datos.

De esta forma está consultando todos los M3 objetos solo una vez, lo que mejora el rendimiento.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *