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

En doc Django,

select_related() “sigue” 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?

Mi entendimiento es que para la relación de clave externa, use select_related ; y para la relación M2M, use prefetch_related . ¿Es esto correcto?

Su comprensión es mayormente correcta. Utiliza select_related cuando el objeto que va a seleccionar es un solo objeto, por OneToOneField tanto, OneToOneField o ForeignKey . prefetch_related cuando vas a obtener un “conjunto” de cosas, por lo que ManyToManyField s como lo ManyToManyField o revertir ForeignKey s. Solo para aclarar lo que quiero decir con “reversa ForeignKey s” 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 hace una unión SQL y, por lo tanto, recupera los resultados como parte de la tabla del servidor SQL. prefetch_related por otra parte 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 que pueda usar select_related .

Las ventajas y desventajas 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 entiendo que Django siempre envía una lista y dice SELECCIONAR … DONDE ENTRAR pk (…, …, …) básicamente. En este caso, si los datos precargados son escasos (digamos los 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, 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. En el lado de Python, sin embargo, 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 los objetos duplicados se crearán en Python para cada objeto “principal”. Como 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, para evitar consultas db 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 memoria de forma preventiva en lugar de realizar consultas a pedido 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 de uno a uno, el número de filas no boostá. Sin embargo, las uniones de muchos a muchos no tienen esta garantía. Por lo tanto, Django restringe select_related a las relaciones que no resultarán inesperadamente en una unión masiva.

La “unión en python” para prefetch_related es un poco más alarmante de lo que debería ser. Crea una consulta separada 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 unión con potencialmente demasiadas filas, cada tabla se divide en una consulta separada.