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
, этот метод будет работать во всех областях, в которых может быть применена арифметическая операция.