Django
Wyrażenia F ()
Szukaj…
Wprowadzenie
Wyrażenie F () jest sposobem na użycie przez Django obiektu Python w celu odniesienia się do wartości pola modelu lub kolumny z adnotacjami w bazie danych bez konieczności pobierania wartości do pamięci Python. Dzięki temu programiści mogą unikać określonych warunków wyścigu, a także filtrować wyniki na podstawie wartości pola modelu.
Składnia
- z importu django.db.models F
Unikanie warunków wyścigu
Zobacz to pytanie, jeśli nie wiesz, jakie są warunki wyścigu.
Poniższy kod może podlegać warunkom wyścigu:
article = Article.objects.get(pk=69)
article.views_count += 1
article.save()
Jeśli liczba views_count
jest równa 1337
, spowoduje to takie zapytanie:
UPDATE app_article SET views_count = 1338 WHERE id=69
Jeśli dwóch klientów uzyskuje dostęp do tego artykułu w tym samym czasie, może się zdarzyć, że drugie żądanie HTTP wykona Article.objects.get(pk=69)
przed pierwszym uruchomieniem article.save()
. Zatem oba żądania będą miały wartość views_count = 1337
, views_count = 1337
ją i views_count = 1338
w bazie danych, podczas gdy faktycznie powinna to być 1339
.
Aby to naprawić, użyj wyrażenia F()
:
article = Article.objects.get(pk=69)
article.views_count = F('views_count') + 1
article.save()
To z kolei spowoduje takie zapytanie:
UPDATE app_article SET views_count = views_count + 1 WHERE id=69
Zbiorcza aktualizacja zestawu zapytań
Załóżmy, że chcemy usunąć 2 głosy poparcia ze wszystkich artykułów autora o id 51
.
Wykonanie tego tylko przy użyciu Pythona spowodowałoby wykonanie N
zapytań ( N
oznacza liczbę artykułów w zestawie zapytań):
for article in Article.objects.filter(author_id=51):
article.upvotes -= 2
article.save()
# Note that there is a race condition here but this is not the focus
# of this example.
Co jeśli zamiast wciągania wszystkich artykułów do Pythona, zapętlania ich, zmniejszania głosów pozytywnych i zapisywania każdego zaktualizowanego z powrotem do bazy danych, istnieje inny sposób?
Używając wyrażenia F()
, możesz to zrobić w jednym zapytaniu:
Article.objects.filter(author_id=51).update(upvotes=F('upvotes') - 2)
Które można przetłumaczyć w następującym zapytaniu SQL:
UPDATE app_article SET upvotes = upvotes - 2 WHERE author_id = 51
Dlaczego to jest lepsze?
- Zamiast wykonywać pracę w Pythonie, przekazujemy obciążenie do bazy danych, która jest dostosowana do wykonywania takich zapytań.
- Skutecznie zmniejsza liczbę zapytań do bazy danych potrzebnych do osiągnięcia pożądanego rezultatu.
Wykonuj operacje arytmetyczne między polami
F()
można używać do wykonywania operacji arytmetycznych ( +
, -
, *
itd.) Między polami modelu, w celu zdefiniowania wyszukiwania algebraicznego / połączenia między nimi.
Niech model będzie:
class MyModel(models.Model): int_1 = models.IntegerField() int_2 = models.IntegerField()
Załóżmy teraz, że chcemy pobrać wszystkie obiekty tabeli
MyModel
które sąint_1
iint_2
spełniają to równanie:int_1 + int_2 >= 5
. Używającannotate()
ifilter()
otrzymujemy:result = MyModel.objects.annotate( diff=F(int_1) + F(int_2) ).filter(diff__gte=5)
result
zawiera teraz wszystkie wyżej wymienione obiekty.
Chociaż w przykładzie wykorzystano pola Integer
, ta metoda będzie działać na każdym polu, na którym można zastosować operację arytmetyczną.