Django F() expressions Updating queryset in bulk


Let's assume that we want to remove 2 upvotes from all the articles of the author with id 51.
Doing this only with Python would execute N queries (N being the number of articles in the queryset):

for article in Article.objects.filter(author_id=51):
    article.upvotes -= 2
    # Note that there is a race condition here but this is not the focus
    # of this example.

What if instead of pulling all the articles into Python, looping over them, decreasing the upvotes, and saving each updated one back to the database, there was another way?
Using an F() expression, can do it in one query:

Article.objects.filter(author_id=51).update(upvotes=F('upvotes') - 2)

Which can be translated in the following SQL query:

UPDATE app_article SET upvotes = upvotes - 2 WHERE author_id = 51

Why is this better?

  • Instead of Python doing the work, we pass the load into the database which is fine tuned to make such queries.
  • Effectively cuts down on the number of database queries needed to achieve the wanted result.