En Django doc,
select_related()
"sigue" las relaciones de clave externa, seleccionando datos adicionales de objetos relacionados 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 extranjera, use select_related
; y para la relación M2M, use prefetch_related
. ¿Es esto correcto?
python
django
django-models
django-orm
NeoWang
fuente
fuente
Respuestas:
Tu comprensión es mayormente correcta. Usas
select_related
cuando el objeto que vas a seleccionar es un solo objeto, por lo queOneToOneField
o aForeignKey
. Usasprefetch_related
cuando vas a obtener un "conjunto" de cosas, por lo queManyToManyField
s como dijiste o reviertesForeignKey
s. Solo para aclarar lo que quiero decir con "ForeignKey
s inversa " aquí hay un ejemplo:La diferencia es que se
select_related
une 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). Puede usarprefetch_related
para cualquier cosa que pueda usarselect_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 SELECCIONAR ... DONDE 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 la ventaja adicional de que se utiliza un solo objeto para representar cada objeto en la base de datos. Conselect_related
objetos duplicados se crearán 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.fuente
select_related
es una consulta mientrasprefetch_related
que dos son, por lo que la primera es más rápida. Peroselect_related
no te ayudará porManyToManyField
'sselect_related
utiliza un JOIN en el SQL mientrasprefetch_related
ejecuta la consulta en el primer modelo, recopila todas las ID que necesita para buscar previamente y luego ejecuta una consulta con una cláusula IN en WHERE con todas las ID que necesita. Si ha dicho 3-5 modelos que usan la misma clave foránea,select_related
seguramente será mejor. Si tiene cientos o miles de modelos que usan la misma clave externa, enprefetch_related
realidad podría ser mejor. En el medio tendrás que probar y ver qué pasa.Ambos métodos logran el mismo propósito, renunciar a consultas de 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 se restringe
select_related
a las relaciones que inesperadamente no resultarán en 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:En lugar de realizar una sola unión con potencialmente demasiadas filas, cada tabla se divide en una consulta separada.
fuente
Como dice la documentación de Django:
Más información sobre esto: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related
fuente
Pasó por las respuestas ya publicadas. Solo pensé que sería mejor si agrego una respuesta con un ejemplo real.
Digamos que tienes 3 modelos de Django que están relacionados.
Aquí puede consultar el
M2
modelo y susM1
objetos relativos usando elselect_relation
campo y losM3
objetos usando elprefetch_relation
campo.Sin embargo, como hemos mencionado
M1
, la relación deM2
es aForeignKey
, solo devuelve 1 registro para cualquierM2
objeto. Lo mismo se aplicaOneToOneField
también.Pero
M3
la relación deM2
es unaManyToManyField
que podría devolver cualquier número deM1
objetos.Considere un caso en el que tiene 2
M2
objetosm21
,m22
que tienen los mismos 5M3
objetos asociados con ID1,2,3,4,5
. Cuando buscaM3
objetos asociados para cada uno de esosM2
objetos, si usa select related, así es como funcionará.Pasos:
m21
objeto.M3
objetos relacionados con elm21
objeto cuyos ID son1,2,3,4,5
.m22
objeto y todos los demásM2
objetos.Como tenemos mismas
1,2,3,4,5
identificaciones tantom21
,m22
los objetos, si utilizamos la opción select_related, que va a consultar la base de datos dos veces por los mismos identificadores que ya eran descabellada.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 consultar laM2
tabla y, como último paso, Django hará una consulta a laM3
tabla con el conjunto de todas las ID que susM2
objetos han devuelto. y unirlos aM2
objetos usando Python en lugar de la base de datos.De esta manera, está consultando todos los
M3
objetos solo una vez, lo que mejora el rendimiento.fuente