suponha que eu tenha 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()
Agora, se eu quiser olhar um subconjunto de fotos em um subconjunto de álbuns de forma eficiente. Eu faço algo assim:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
Isso faz apenas duas consultas, que é o que eu espero (uma para obter os álbuns e outra como `SELECT * IN photos WHERE photoalbum_id IN ().
Tudo é bom.
Mas se eu fizer isso:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.filter(format=1)
Em seguida, ele faz uma tonelada de consultas com WHERE format = 1
! Estou fazendo algo errado ou o Django não é inteligente o suficiente para perceber que já pegou todas as fotos e pode filtrá-las em python? Juro que li em algum lugar da documentação que é para fazer isso ...
Respostas:
No Django 1.6 e anteriores, não é possível evitar as consultas extras. A
prefetch_related
chamada efetivamente armazena em cache os resultados dea.photoset.all()
para cada álbum no queryset. No entanto,a.photoset.filter(format=1)
é um queryset diferente, então você irá gerar uma consulta extra para cada álbum.Isso é explicado nos
prefetch_related
documentos. Ofilter(format=1)
é equivalente afilter(spicy=True)
.Observe que você pode reduzir o número de consultas filtrando as fotos em python:
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]
No Django 1.7, existe um
Prefetch()
objeto que permite controlar o comportamento 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 obter mais exemplos de como usar o
Prefetch
objeto, consulte aprefetch_related
documentação.fonte
Dos documentos :
No seu caso, "a.photo_set.filter (format = 1)" é tratado como uma nova consulta.
Além disso, "photo_set" é uma pesquisa reversa - implementada por meio de um gerenciador diferente.
fonte
photo_set
também pode ser pré-obtido com.prefetch_related('photo_set')
. Mas a ordem é importante, como você explicou.Pode-
select_related
se usar se você quiser com filtro ()results = Geography.objects.filter(state__pk = 1).select_related('country') results.query
Para mais informações: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#select-related
fonte