Django gives you two ways to change rows in the database:
instance.save() — runs your model's save() method, fires pre_save / post_save signals, refreshes auto_now fields, and writes the whole row. Use it when you want the model's full lifecycle.Model.objects.filter(...).update(field=...) — emits a single SQL UPDATE and changes only the columns you name. It bypasses save(), signals, and auto_now.The choice isn't about performance — it's about whether you want the lifecycle to run. If your save() validates, normalizes, or has side effects you don't want repeated for a narrow state change, .update() is the right tool.
In this repo, UserFeed.save() re-validates the URL and raises ValueError on bad input. When crawl_feed records "I just fetched this," it only wants to bump scheduling fields — re-running URL validation would be wrong:
UserFeed.objects.filter(pk=feed.pk).update(
last_fetched_at=fetched_at,
next_check_at=fetched_at + HEALTHY_INTERVAL,
)
A trap: .update() does not update auto_now fields. If you rely on updated_at being touched, use .save() or set it manually.
.save() you could replace with .update() — pick one in this repo where only one or two fields change and the model's save() lifecycle is irrelevant. Write down what you'd lose by switching (signals? auto_now?).
Done when: you have a one-paragraph note in this file describing the trade-off for that specific call site.auto_now=True on a field, then .update() a different field. Confirm the auto_now field is not refreshed.
Done when: you can explain why auto_now and .update() don't mix in one sentence.