przypuśćmy, że mam ten model:
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()
Teraz, jeśli chcę efektywnie przeglądać podzbiór zdjęć w podzbiorze albumów. Robię to mniej więcej tak:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
Robi to tylko dwa zapytania, czego się spodziewam (jedno do uzyskania albumów, a następnie jedno w stylu `SELECT * IN photos WHERE photoalbum_id IN ().
Wszystko w porządku.
Ale jeśli to zrobię:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.filter(format=1)
Następnie wykonuje mnóstwo zapytań WHERE format = 1
! Czy robię coś źle, czy też django nie jest na tyle sprytne, aby zdać sobie sprawę, że pobrał już wszystkie zdjęcia i może je filtrować w Pythonie? Przysięgam, że gdzieś w dokumentacji przeczytałem, że ma to robić ...
Odpowiedzi:
W Django 1.6 i wcześniejszych nie można uniknąć dodatkowych zapytań.
prefetch_related
Wezwanie skutecznie buforuje rezultatya.photoset.all()
dla każdego albumu w queryset. Jednaka.photoset.filter(format=1)
jest to inny zestaw zapytań, więc dla każdego albumu wygenerujesz dodatkowe zapytanie.Jest to wyjaśnione w
prefetch_related
dokumentacji.filter(format=1)
Jest równoważnafilter(spicy=True)
.Zauważ, że możesz zmniejszyć liczbę lub zapytań, filtrując zdjęcia w Pythonie:
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]
W Django 1.7 istnieje
Prefetch()
obiekt, który pozwala kontrolować zachowanieprefetch_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
Więcej przykładów użycia
Prefetch
obiektu można znaleźć wprefetch_related
dokumentacji.źródło
Z dokumentów :
W twoim przypadku „a.photo_set.filter (format = 1)” jest traktowane jak nowe zapytanie.
Ponadto „photo_set” jest wyszukiwaniem wstecznym - zaimplementowanym przez zupełnie innego menedżera.
źródło
photo_set
można również wstępnie pobrać za pomocą.prefetch_related('photo_set')
. Ale porządek ma znaczenie, jak wyjaśniłeś.Można użyć,
select_related
jeśli chcesz używać go z filtrem ()results = Geography.objects.filter(state__pk = 1).select_related('country') results.query
Więcej: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#select-related
źródło