Django
Выражения F ()
Поиск…
Вступление
Выражение F () - это способ Django использовать объект Python для ссылки на значение поля модели или аннотированного столбца в базе данных без необходимости вытягивать значение в память Python. Это позволяет разработчикам избегать определенных условий гонки, а также фильтровать результаты на основе значений полей модели.
Синтаксис
- из django.db.models import F
Избежать условий гонки
См. Этот вопрос Q & A, если вы не знаете, какие условия гонки.
Следующий код может подпадать под условия гонки:
article = Article.objects.get(pk=69)
article.views_count += 1
article.save()
Если views_count равен 1337 , это приведет к такому запросу:
UPDATE app_article SET views_count = 1338 WHERE id=69
Если два клиента одновременно обращаются к этой статье, может произойти, что второй HTTP-запрос выполняет Article.objects.get(pk=69) прежде чем первый выполнит article.save() . Таким образом, оба запроса будут иметь views_count = 1337 , увеличивают его и сохраняют views_count = 1338 в базе данных, тогда как на самом деле это должно быть 1339 .
Чтобы исправить это, используйте выражение F() :
article = Article.objects.get(pk=69)
article.views_count = F('views_count') + 1
article.save()
Это, с другой стороны, приведет к такому запросу:
UPDATE app_article SET views_count = views_count + 1 WHERE id=69
Обновление набора запросов навалом
Предположим, что мы хотим удалить 2 upvotes из всех статей автора с идентификатором 51 .
Выполнение этого только с Python будет выполнять N запросов ( N - количество статей в наборе запросов):
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.
Что делать, если вместо того, чтобы вытаскивать все статьи в Python, перебирать их, уменьшать upvotes и сохранять каждый обновленный обратно в базу данных, был другой способ?
Используя выражение F() , можно сделать это в одном запросе:
Article.objects.filter(author_id=51).update(upvotes=F('upvotes') - 2)
Что можно перевести в следующем SQL-запросе:
UPDATE app_article SET upvotes = upvotes - 2 WHERE author_id = 51
Почему это лучше?
- Вместо того, чтобы Python выполнял работу, мы передаем нагрузку в базу данных, которая настроена так, чтобы делать такие запросы.
- Эффективно сокращает количество запросов к базе данных, необходимых для достижения желаемого результата.
Выполнение арифметических операций между полями
Выражения F() могут использоваться для выполнения арифметических операций ( + , - , * и т. Д.) Среди полей модели, чтобы определить алгебраический поиск / связь между ними.
Пусть модель будет:
class MyModel(models.Model): int_1 = models.IntegerField() int_2 = models.IntegerField()Теперь давайте предположим , что мы хотим получить все объекты
MyModelтаблицы Кто наint_1иint_2поля удовлетворяют этому уравнению:int_1 + int_2 >= 5. Используяannotate()иfilter()получаем:result = MyModel.objects.annotate( diff=F(int_1) + F(int_2) ).filter(diff__gte=5)resultтеперь содержит все вышеупомянутые объекты.
Хотя в этом примере используются поля Integer , этот метод будет работать во всех областях, в которых может быть применена арифметическая операция.