DEV Community

Erlan Akbaraliev
Erlan Akbaraliev

Posted on

Django F() expression

F() is an object used to avoid RACE CONDITION in the database.

reporter = Reporters.objects.get(name='Erlan')
reporter.stories_filed += 1
reporter.save()
Enter fullscreen mode Exit fullscreen mode
from django.db.models import F

reporter = Reporters.objects.get(name='Erlan')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
Enter fullscreen mode Exit fullscreen mode

In the first version we're pulling out the value of stories_filed from the database and and making changes, Read-Modify-Save, VULNERABLE.
In the second version we're using F() to get the SQL expression to refer to the field, yes we refer to the field, we don't pull out the actual value when using F(), and performing Atomic Update, SAFE.

Imaging the scenario:

  • Thread A retrieves Erlan (stories = 10)
  • Thread B retrieves Erlan (stories = 10)
  • Thread A calculates $10 + 1 = 11$ and saves
  • Thread B calculates $10 + 1 = 11$ and saves
  • Result: The count is 11, but it should be 12

By using F() we're locking the data, reporter.stories_filed, for writing. After we finish writing the data into the field, the data will be unlocked and others will be able to write to the field.
So we ensure Data Integrity.

We can also lock and modify a field using update():

from django.db.models import F

reporter = Reporters.objects.filter(name='Erlan')
reporter.update(stories_filed = F('stories_filed') + 1)
Enter fullscreen mode Exit fullscreen mode

Important: to get the reporter 'Erlan', I am using filter, because update can be used only to update bulk data or in other words it can be used to update only objects of type QuerySet.
In our case, we know there's only one reporter named 'Erlan', so inside of the QuerySet we will have only 1 reporter.

Top comments (0)