Dlaczego prefetch related () django działa tylko z all () a nie filter ()?
Załóż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 spojrzeć na podzbiór zdjęć w podzbiorze albumów. Robię to coś takiego:
someAlbums = PhotoAlbum.objects.filter(author="Davey Jones").prefetch_related("photo_set")
for a in someAlbums:
somePhotos = a.photo_set.all()
To wykonuje tylko dwa zapytania, czyli to czego oczekuję (jedno, aby uzyskać albumy, a następnie jedno, jak ' SELECT * in photos WHERE photoalbum_id in ().
Wszystko jest świetne.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ń z WHERE format = 1
! Czy robię coś nie tak czy django nie jesteś na tyle mądry, aby zdać sobie sprawę, że już pobrał Wszystkie zdjęcia i może je filtrować w Pythonie? Przysięgam, że czytałem gdzieś w dokumentacji, że ma to zrobić...
3 answers
W Django 1.6 i wcześniejszych, nie jest możliwe uniknięcie dodatkowych zapytań. Wywołanie prefetch_related
skutecznie buforuje wyniki a.photoset.all()
dla każdego albumu w querysecie. Jednakże a.photoset.filter(format=1)
jest innym zestawem zapytań, więc wygenerujesz dodatkowe zapytanie dla każdego albumu.
Jest to wyjaśnione w prefetch_related
docs. filter(format=1)
jest równoważne filter(spicy=True)
.
Zauważ, że możesz zmniejszyć liczbę lub zapytania, 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, jest Prefetch()
obiekt, który pozwala kontrolować zachowanie 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
Więcej przykładów użycia obiektu Prefetch
można znaleźć w prefetch_related
docs.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 08:14:12
Z docs :
... Jak zawsze w przypadku zestawów zapytań, wszelkie kolejne metody łańcuchowe, które implikują inne zapytanie do bazy danych, ignorują wcześniej buforowane wyniki i pobierają dane za pomocą świeżego zapytania do bazy danych. Tak więc, jeśli napiszesz co następuje:
pizzas = Pizza.objects.prefetch_related('toppings')
[list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
...potem fakt, że pizza.dodatki.all () zostało wstępnie ustawione nie pomoże Ci - w rzeczywistości szkodzi wydajności, ponieważ wykonałeś zapytanie do bazy danych, którego nie użyłeś. Więc użyj tej funkcji z Uwaga!
W Twoim przypadku, " a. photo_set.filter (format = 1) " jest traktowany jak świeże zapytanie.
Dodatkowo, "photo_set" jest odwrotnym wyszukiwaniem-zaimplementowanym za pomocą innego menedżera.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55
Można użyć select_related
, jeśli chcesz go użyć z filter ()
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-08 11:39:04