предположим, у меня есть такая модель:
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()
Теперь, если я хочу эффективно просматривать подмножество фотографий в подмножестве альбомов. Я делаю это примерно так:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
При этом выполняется только два запроса, как я и ожидал (один для получения альбомов, а второй - типа `SELECT * IN photos WHERE photoalbum_id IN ().
Все прекрасно.
Но если я сделаю это:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.filter(format=1)
Затем он выполняет множество запросов с помощью WHERE format = 1
! Я что-то делаю не так или django недостаточно умен, чтобы понять, что он уже получил все фотографии и может фильтровать их в python? Клянусь, я где-то читал в документации, что он должен это делать ...
Ответы:
В Django 1.6 и ранее избежать лишних запросов невозможно.
prefetch_related
Вызов эффективно кэширует результатыa.photoset.all()
для каждого альбома в QuerySet. Однакоa.photoset.filter(format=1)
это другой набор запросов, поэтому вы создадите дополнительный запрос для каждого альбома.Это объясняется в
prefetch_related
документации.filter(format=1)
Эквивалентноfilter(spicy=True)
.Обратите внимание, что вы можете уменьшить количество запросов, отфильтровав фотографии в 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]
В Django 1.7 есть
Prefetch()
объект, который позволяет вам управлять поведениемprefetch_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
Дополнительные примеры использования
Prefetch
объекта см. Вprefetch_related
документации.источник
Из документов :
В вашем случае «a.photo_set.filter (format = 1)» обрабатывается как новый запрос.
Кроме того, «photo_set» - это обратный поиск, реализованный через другой менеджер.
источник
photo_set
также можно предварительно загрузить с помощью.prefetch_related('photo_set')
. Но порядок имеет значение, как вы объяснили.Можно использовать,
select_related
если вы хотите использовать его с filter ()results = Geography.objects.filter(state__pk = 1).select_related('country') results.query
Подробнее: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#select-related
источник