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
MyModelktóre sąint_1iint_2speł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)resultzawiera 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ą.