supongamos que tengo este modelo:
class PhotoAlbum(models.Model):
title = models.CharField(max_length=128)
author = models.CharField(max_length=128)
class Photo(models.Model):
album = models.ForeignKey('PhotoAlbum')
format = models.IntegerField()
Ahora, si quiero ver un subconjunto de fotos en un subconjunto de álbumes de manera eficiente. Lo hago algo como esto:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
Esto hace solo dos consultas, que es lo que espero (una para obtener los álbumes y luego otra como `SELECT * IN photos WHERE photoalbum_id IN ().
Todo esta bien.
Pero si hago esto:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.filter(format=1)
¡Entonces hace un montón de consultas con WHERE format = 1
! ¿Estoy haciendo algo mal o django no es lo suficientemente inteligente como para darse cuenta de que ya ha obtenido todas las fotos y puede filtrarlas en Python? Juro que leí en algún lugar de la documentación que se supone que debe hacer eso ...
Respuestas:
En Django 1.6 y versiones anteriores, no es posible evitar las consultas adicionales. La
prefetch_related
llamada almacena en caché de forma efectiva los resultados dea.photoset.all()
para cada álbum del conjunto de consultas. Sin embargo,a.photoset.filter(format=1)
es un conjunto de consultas diferente, por lo que generará una consulta adicional para cada álbum.Esto se explica en los
prefetch_related
documentos. Elfilter(format=1)
es equivalente afilter(spicy=True)
.Tenga en cuenta que podría reducir el número de consultas filtrando las fotos en Python en su lugar:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set") for a in someAlbums: somePhotos = [p for p in a.photo_set.all() if p.format == 1]
En Django 1.7, hay un
Prefetch()
objeto que te permite controlar el comportamiento deprefetch_related
.from django.db.models import Prefetch someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related( Prefetch( "photo_set", queryset=Photo.objects.filter(format=1), to_attr="some_photos" ) ) for a in someAlbums: somePhotos = a.some_photos
Para obtener más ejemplos de cómo utilizar el
Prefetch
objeto, consulte losprefetch_related
documentos.fuente
De los documentos :
En su caso, "a.photo_set.filter (format = 1)" se trata como una consulta nueva.
Además, "photo_set" es una búsqueda inversa, implementada a través de un administrador diferente.
fuente
photo_set
también se puede obtener previamente con.prefetch_related('photo_set')
. Pero el orden importa, como ha explicado.Se puede usar
select_related
si quieres usarlo con filter ()results = Geography.objects.filter(state__pk = 1).select_related('country') results.query
Para más: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#select-related
fuente