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ć...

Author: Kevin Christopher Henry, 2012-10-19

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.

 171
Author: Alasdair,
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.

 8
Author: Ngure Nyaga,
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

 0
Author: Stuti Verma,
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