W dokumencie Django
select_related()
„śledzi” relacje z kluczem obcym, wybierając dodatkowe dane obiektu pokrewnego podczas wykonywania zapytania.
prefetch_related()
wykonuje osobne wyszukiwanie dla każdej relacji i wykonuje „łączenie” w Pythonie.
Co to znaczy „robić łączenie w pythonie”? Czy ktoś może zilustrować przykładem?
Rozumiem, że w przypadku relacji klucza obcego użyj select_related
; i dla relacji M2M użyj prefetch_related
. Czy to jest poprawne?
python
django
django-models
django-orm
NeoWang
źródło
źródło
Odpowiedzi:
Twoje rozumienie jest w większości poprawne. Używasz,
select_related
gdy obiekt, który chcesz wybrać, jest pojedynczym obiektem, takOneToOneField
lub aForeignKey
. Używasz,prefetch_related
gdy masz „zbiór” rzeczy, takManyToManyField
jak powiedziałeś lub odwróciłeśForeignKey
. Aby wyjaśnić, co rozumiem przez „rewersjęForeignKey
”, oto przykład:Różnica polega na tym,
select_related
że łączy SQL i dlatego zwraca wyniki jako część tabeli z serwera SQL.prefetch_related
z drugiej strony wykonuje inne zapytanie i dlatego redukuje zbędne kolumny w oryginalnym obiekcie (ModelA
w powyższym przykładzie). Możesz użyćprefetch_related
do wszystkiego, czego możesz użyćselect_related
.Do kompromisów należy
prefetch_related
utworzenie i wysłanie listy identyfikatorów, aby wybrać je z powrotem na serwer, może to chwilę potrwać. Nie jestem pewien, czy istnieje dobry sposób na zrobienie tego w transakcji, ale rozumiem, że Django zawsze po prostu wysyła listę i mówi WYBIERZ ... GDZIE pk IN (..., ..., ...) gruntownie. W takim przypadku, jeśli wstępnie pobrane dane są rzadkie (powiedzmy obiekty stanu USA powiązane z adresami osób), może to być bardzo dobre, jednak jeśli jest bliżej jeden do jednego, może to zmarnować dużo komunikacji. W razie wątpliwości wypróbuj jedno i drugie i sprawdź, który z nich działa lepiej.Wszystko omówione powyżej dotyczy w zasadzie komunikacji z bazą danych. Po stronie Pythona
prefetch_related
ma jednak tę dodatkową zaletę, że do reprezentowania każdego obiektu w bazie danych służy jeden obiekt. Zeselect_related
zduplikowanymi obiektami zostaną utworzone w Pythonie dla każdego „rodzica” obiektu. Ponieważ obiekty w Pythonie mają sporo miejsca w pamięci, może to również być brane pod uwagę.źródło
select_related
to jedno zapytanie, podczas gdyprefetch_related
dwa, więc pierwsze jest szybsze. Aleselect_related
nie pomoże wManyToManyField
„sselect_related
używa JOIN w SQL, podczas gdyprefetch_related
uruchom zapytanie w pierwszym modelu, zbiera wszystkie identyfikatory potrzebne do pobrania z wyprzedzeniem, a następnie uruchamia zapytanie z klauzulą IN w GDZIE ze wszystkimi potrzebnymi identyfikatorami. Jeśli powiesz, że 3-5 modeli używa tego samego klucza obcego,select_related
prawie na pewno będzie lepiej. Jeśli masz setki lub tysiące modeli używających tego samego klucza obcego,prefetch_related
może być lepiej. W międzyczasie będziesz musiał przetestować i zobaczyć, co się stanie.Obie metody osiągają ten sam cel, aby zrezygnować z niepotrzebnych zapytań db. Ale używają różnych podejść do wydajności.
Jedynym powodem użycia jednej z tych metod jest to, że jedno duże zapytanie jest lepsze niż wiele małych zapytań. Django używa dużego zapytania, aby zapobiegawczo tworzyć modele w pamięci zamiast wykonywać zapytania na żądanie względem bazy danych.
select_related
wykonuje łączenie przy każdym wyszukiwaniu, ale rozszerza zaznaczenie, aby uwzględnić kolumny wszystkich połączonych tabel. Jednak takie podejście ma pewne zastrzeżenie.Połączenia mogą potencjalnie pomnożyć liczbę wierszy w zapytaniu. Kiedy wykonujesz sprzężenie na kluczu obcym lub polu jeden do jednego, liczba wierszy nie wzrośnie. Jednak połączenia wielu do wielu nie mają tej gwarancji. Django ogranicza
select_related
do relacji, które nieoczekiwanie nie spowodują masowego łączenia.The „Przyłączyć pytona” dla
prefetch_related
trochę bardziej niepokojące to powinno być. Tworzy osobne zapytanie dla każdej tabeli, która ma zostać połączona. Filtruje każdą z tych tabel za pomocą klauzuli WHERE IN, na przykład:Zamiast wykonywania pojedynczego łączenia z potencjalnie zbyt wieloma wierszami, każda tabela jest podzielona na osobne zapytanie.
źródło
Jak mówi dokumentacja Django:
Więcej informacji na ten temat: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related
źródło
Przejrzał już opublikowane odpowiedzi. Pomyślałem, że byłoby lepiej, jeśli dodam odpowiedź z faktycznym przykładem.
Powiedzmy, że masz 3 powiązane modele Django.
Tutaj możesz zapytać o
M2
model i jego względneM1
obiekty za pomocąselect_relation
pola iM3
obiektów za pomocąprefetch_relation
pola.Jednak, jak wspomnieliśmy
M1
, relacja zM2
jestForeignKey
, to zwraca tylko 1 rekord dla dowolnegoM2
obiektu. To samo dotyczyOneToOneField
również.Ale
M3
relacja zM2
jest tą,ManyToManyField
która może zwrócić dowolną liczbęM1
obiektów.Rozważ przypadek, w którym masz 2
M2
przedmiotym21
,m22
które mają takie same 5M3
obiektów powiązanych z identyfikatorami1,2,3,4,5
. Podczas pobierania powiązanychM3
obiektów dla każdego z nichM2
obiektów, jeśli użyjesz opcji „Wybierz powiązane”, tak to będzie działać.Kroki:
m21
obiekt.M3
obiekty związane zm21
obiektami, których identyfikatory to1,2,3,4,5
.m22
obiektu i wszystkich innychM2
obiektów.Ponieważ mamy te same
1,2,3,4,5
identyfikatory dla obum21
,m22
obiektów, jeśli używamy opcji select_related, to będzie kwerendy DB dwukrotnie za te same identyfikatory, które były już naciągane.Zamiast tego, jeśli użyjesz parametru prefetch_related, przy próbie uzyskania
M2
obiektów zanotuje wszystkie identyfikatory zwrócone przez Twoje obiekty (Uwaga: tylko identyfikatory) podczas zapytania doM2
tabeli, a na ostatnim etapie Django wykona zapytanie doM3
tabeli z zestawem wszystkich twoich identyfikatorówM2
zwróconych przez obiekty. i dołącz do nichM2
obiektów za pomocą Pythona zamiast bazy danych.W ten sposób odpytujesz wszystkie
M3
obiekty tylko raz, co poprawia wydajność.źródło