Need to set the same value on hundreds or thousands of rows? Don’t loop through each object and call save() — use QuerySet.update() so Django folds everything into exactly one SQL UPDATE. It is far faster, avoids race conditions when combined with F(), and fits bulk jobs best: changing statuses or decrementing stock.

The Problem

Say you need to publish 1,000 draft articles. The first version many people write is a loop:

for article in Article.objects.filter(status="draft"):
article.status = "published"
article.save()

This code runs correctly but wastes a lot: each iteration costs one SELECT to load the object plus one UPDATE to save it — 1,000 articles means 2,000 queries, plus 1,000 objects held in RAM. Worse, between loading the object and calling save(), another request may edit the same row — and save() silently overwrites it. This bug only shows up when traffic grows, and it is painful to debug.

The Solution

One line replaces the whole loop:

updated = Article.objects.filter(status="draft").update(status="published")
print(updated) # number of updated rows, e.g.: 1000

update() runs exactly one UPDATE, loads no objects into RAM, and returns the number of affected rows — handy for logging or checking the result. To change several columns at once, pass more arguments:

from django.utils import timezone
Article.objects.filter(status="draft").update(
status="published",
published_at=timezone.now(),
)

Decrementing stock is the second classic case. Don’t read stock into Python, subtract, and save it back — use F() so the arithmetic happens inside SQL, atomically at the database level:

from django.db.models import F
Product.objects.filter(id=42).update(stock=F("stock") - 1)

Even if 10 requests decrement stock at the same time, each subtraction is computed on the freshest value in the database, with nothing lost. Note that update() only touches columns of the main table — it cannot update fields of a related model — but you can still filter across a relationship, for example Entry.objects.filter(blog__id=1).update(comments_on=True).

Apply It to Your Project

Next time you write a management command to clean up data, a cron job to change order statuses, or an API endpoint to “mark all as read”, check whether you are looping with save(). If every row gets the same value, update() is almost always the right answer — fewer queries, faster, and safer when many requests run at once. Just remember two limits: update() does not call save(), so it fires no signals, and auto_now does not change on its own — set the timestamp explicitly in the statement when you need it.

Related Posts

  • Django: create and update in bulk with bulk_create/bulk_update instead of save() in a loop
  • Django: update the right column with update_fields — leaner queries, no more overwritten data in 5 minutes
  • Django: use get_or_create() and update_or_create() the right way — fetch or create a record in a single line

Frequently Asked Questions

Does update() call save() or fire signals?

No. update() writes directly at the SQL level, so it does not call save(), does not fire pre_save/post_save, and does not run full_clean(). For models with logic in save() or in signals, you have to loop and call save() on each object.

How is update() different from bulk_update()?

update() assigns the same value to every row matching filter(), in a single SQL statement. bulk_update() assigns a different value to each object you pass in — use it when every record needs its own value.

Can it update fields of a related model?

No. update() only touches columns of the main table — Entry.objects.update(blog__name="foo") raises an error, for example. To change data on a related model, update that model’s own queryset directly.

Does an auto_now field change when using update()?

No. Because update() skips save(), an auto_now field keeps its old value. To update the timestamp too, pass it explicitly, for example update(status="published", updated_at=timezone.now()).

When should I still use a save() loop?

When each object needs its own logic in save(), when you need signals to sync a cache or send notifications, or when you need validation before saving. For anything else — bulk updates where every row gets the same value — update() is always faster.

References