Поиск…


Вступление

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow