python - query - select django
¿Cuál es la diferencia entre select_related y prefetch_related en Django ORM? (2)
En Django doc,
select_related()
"sigue" las relaciones de clave externa, seleccionando datos de objetos relacionados adicionales cuando ejecuta su consulta.
prefetch_related()
realiza una búsqueda por separado para cada relación y realiza la "unión" en Python.
¿Qué significa "hacer la unión en python"? ¿Alguien puede ilustrar con un ejemplo?
Entiendo que para una relación de clave externa, use
select_related
;
y para la relación M2M, use
prefetch_related
.
¿Es esto correcto?
Ambos métodos logran el mismo propósito, renunciar a consultas db innecesarias. Pero usan diferentes enfoques para la eficiencia.
La única razón para usar cualquiera de estos métodos es cuando una única consulta grande es preferible a muchas consultas pequeñas. Django utiliza 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 unió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 advertencia.
Las combinaciones tienen el potencial de multiplicar el número de filas en una consulta.
Cuando realiza una unión sobre una clave externa o un campo uno a uno, el número de filas no aumentará.
Sin embargo, las uniones de muchos a muchos no tienen esta garantía.
Entonces, Django restringe
select_related
a las relaciones que inesperadamente no darán lugar a una unión masiva.
El
"join in python"
para
prefetch_related
es un poco más alarmante de lo que debería ser.
Crea una consulta separada para cada tabla 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 sola unión con potencialmente demasiadas filas, cada tabla se divide en una consulta separada.
Tu comprensión es mayormente correcta.
Utiliza
select_related
cuando el objeto que va a seleccionar es un solo objeto, por lo que
OneToOneField
o una
ForeignKey
.
Utiliza
prefetch_related
cuando va a obtener un "conjunto" de cosas, por lo que
ManyToManyField
s como usted indicó o revierte
ForeignKey
s.
Solo para aclarar lo que quiero decir con "
ForeignKey
s inversa" 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, recupera 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).
Puede usar
prefetch_related
para cualquier cosa para la que pueda usar
select_related
.
Las compensaciones son que
prefetch_related
tiene que crear y enviar una lista de ID para seleccionar nuevamente al servidor, esto puede llevar un tiempo.
No estoy seguro de si hay una buena manera de hacer esto en una transacción, pero entiendo que Django siempre envía una lista y dice SELECT ... WHERE pk IN (..., ..., ...) básicamente.
En este caso, si los datos captados previamente 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 discutido 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
objetos duplicados en Python para cada objeto "padre".
Dado que los objetos en Python tienen una carga de memoria decente, esto también puede ser una consideración.