~9 min read • Updated Mar 10, 2026
1. Introduction
Django provides powerful tools for managing database transactions. Transactions ensure that a group of database operations either all succeed or all fail, preserving data integrity.
2. Django’s Default Behavior: Autocommit Mode
By default, Django runs in autocommit mode. This means each SQL query is committed immediately unless it is executed inside an active transaction.
Django automatically uses transactions or savepoints for ORM operations that require multiple queries, such as update() and delete(). Django’s TestCase also wraps each test in a transaction for performance.
3. Per-Request Transactions with ATOMIC_REQUESTS
If you want each HTTP request to run inside a transaction, enable:
ATOMIC_REQUESTS = True
Behavior:
- Before calling the view → Django starts a transaction.
- If the view returns normally → commit.
- If the view raises an exception → rollback.
Important notes:
- This approach is simple but may reduce performance under heavy load.
StreamingHttpResponseruns outside the transaction.- Middleware and template rendering also run outside the transaction.
Opting out for specific views:
@transaction.non_atomic_requests
def my_view(request):
...
4. Explicit Transaction Control with atomic()
atomic() is Django’s primary API for managing transactions.
Features:
- Commits changes if the block exits normally.
- Rolls back changes if an exception occurs.
- Can be nested (inner blocks use savepoints).
- Can enforce durability with
durable=True.
Decorator usage:
@transaction.atomic
def viewfunc(request):
do_stuff()
Context manager usage:
with transaction.atomic():
do_more_stuff()
5. Proper Error Handling with atomic()
The correct pattern is to wrap atomic() inside a try/except, not the other way around.
Correct example:
try:
with transaction.atomic():
generate_relationships()
except IntegrityError:
handle_exception()
Why this matters:
If you catch exceptions inside an atomic block, Django may not detect that the transaction is broken, leading to TransactionManagementError when further queries are attempted.
6. Rollbacks and Model State
When a rollback occurs, Django does not revert the in-memory state of model instances. This can cause inconsistencies.
Example:
obj.active = True
try:
with transaction.atomic():
obj.save()
except DatabaseError:
obj.active = False
For side effects like caching, use transaction.on_commit() to ensure updates happen only after a successful commit.
7. Savepoints and Nested atomic Blocks
Nested atomic blocks behave as follows:
- Entering an inner block → create savepoint.
- Exiting normally → release savepoint.
- Exiting with exception → rollback to savepoint.
You can disable savepoints:
with transaction.atomic(savepoint=False):
...
This reduces overhead but breaks fine-grained error handling.
8. Durability with durable=True
Setting durable=True ensures the block must be the outermost atomic block and commits changes when it exits successfully.
with transaction.atomic(durable=True):
...
If nested, Django raises RuntimeError.
9. Autocommit and Why Django Uses It
In standard SQL, each query starts a transaction that must be explicitly committed or rolled back. This is inconvenient for application developers.
Autocommit simplifies this by wrapping each query in its own transaction:
- Query succeeds → commit.
- Query fails → rollback.
Although PEP 249 requires autocommit to be off by default, Django turns it on to improve developer experience.
Conclusion
Django offers flexible and powerful transaction management tools. atomic() provides precise control, while ATOMIC_REQUESTS enables per-request transactions. Understanding autocommit, savepoints, durability, and proper error handling is essential for building robust, high-performance applications.
1. Deactivating Django’s Transaction Management
You can completely disable Django’s transaction management for a specific database by setting:
AUTOCOMMIT = False
When autocommit is disabled:
- Django will not automatically commit any queries.
- You must manually commit every transaction, including those started by Django or third‑party libraries.
- This is only recommended when implementing custom transaction middleware or unusual database workflows.
2. Performing Actions After a Successful Commit
Sometimes you need to run code only if the current transaction commits successfully—for example:
- sending an email
- triggering a background task
- invalidating a cache
Django provides transaction.on_commit() for this purpose:
from django.db import transaction
def send_welcome_email():
...
transaction.on_commit(send_welcome_email)
Callbacks receive no arguments, but you can bind them using functools.partial():
from functools import partial
for user in users:
transaction.on_commit(partial(send_invite_email, user=user))
Callback behavior:
- Executed only after a successful commit.
- Discarded if the transaction rolls back.
- Executed immediately if no transaction is active.
- Use
robust=Trueto continue executing remaining callbacks even if one fails.
3. Savepoints and on_commit()
Savepoints (created by nested atomic() blocks) interact correctly with on_commit():
Case 1: No rollback → all callbacks run
with transaction.atomic():
transaction.on_commit(foo)
with transaction.atomic():
transaction.on_commit(bar)
# foo() then bar() will run after the outer commit
Case 2: Inner savepoint rolled back → inner callbacks discarded
with transaction.atomic():
transaction.on_commit(foo)
try:
with transaction.atomic():
transaction.on_commit(bar)
raise SomeError()
except SomeError:
pass
# Only foo() runs
4. Execution Order and Error Handling
Callbacks run in the order they were registered.
If a callback registered with robust=False raises an exception:
- remaining callbacks will not run
- the transaction is not rolled back (callbacks run after commit)
5. Timing of Callback Execution
Callbacks run only after autocommit is restored on the connection. This prevents callbacks from accidentally opening new implicit transactions.
When autocommit is active and you are outside any atomic() block, callbacks run immediately.
Important: Calling on_commit() when autocommit is disabled and no atomic block is active raises an error.
6. Using on_commit() in Tests
TestCase wraps each test in a transaction and rolls it back afterward, meaning:
- transactions never commit
on_commit()callbacks never run
Solutions:
TestCase.captureOnCommitCallbacks()— captures callbacks for assertionsTransactionTestCase— commits transactions, but is slower due to database flushes
7. Why Django Has No Rollback Hook
A rollback hook is unreliable because many events can trigger implicit rollbacks, such as:
- database connection drops
- process termination
Instead of doing work and undoing it on rollback, Django encourages delaying the work until commit using on_commit().
8. Low-Level Transaction APIs
These APIs should be used only when implementing custom transaction logic. Prefer atomic() whenever possible.
Autocommit control:
get_autocommit(using=None)
set_autocommit(autocommit, using=None)
Notes:
- Autocommit is on by default.
- Turning it off gives you raw DB-API behavior.
- You must manually restore autocommit.
- You must ensure no transaction is active before re-enabling autocommit.
- Django refuses to disable autocommit inside an
atomic()block.
Manual commit and rollback:
commit(using=None)
rollback(using=None)
Django refuses to commit or roll back inside an active atomic() block to preserve atomicity.
9. What Is a Transaction?
A transaction is an atomic group of database operations. Even if the program crashes, the database guarantees:
- either all changes are applied
- or none of them are
To start a manual transaction, disable autocommit using set_autocommit(False).
Conclusion
Django provides a rich set of tools for advanced transaction control. on_commit() enables safe post‑commit actions, savepoints ensure nested atomicity, and low‑level APIs allow full manual control when needed. Understanding these mechanisms is essential for building robust, consistent, and high‑performance database workflows.
1. What Is a Savepoint?
A savepoint is a marker inside a transaction that allows you to roll back only part of the transaction instead of the entire thing. Savepoints are supported by:
- SQLite
- PostgreSQL
- Oracle
- MySQL (InnoDB engine)
Other backends expose the savepoint API but perform no actual savepoint operations.
2. Savepoints and Autocommit
Savepoints are not useful when autocommit is enabled (Django’s default).
However, inside an atomic() block, Django groups operations into a pending transaction. A full rollback would undo everything, but savepoints allow fine‑grained rollbacks.
3. Savepoints in Nested atomic() Blocks
When atomic() blocks are nested, Django automatically creates savepoints. This allows inner blocks to roll back without affecting the outer block.
Although atomic() is recommended, Django still exposes low‑level savepoint functions.
4. Django’s Savepoint Functions
Create a savepoint:
sid = transaction.savepoint()
Commit a savepoint:
transaction.savepoint_commit(sid)
Roll back to a savepoint:
transaction.savepoint_rollback(sid)
Reset savepoint counter:
transaction.clean_savepoints()
5. Example Usage
@transaction.atomic
def viewfunc(request):
a.save()
sid = transaction.savepoint()
b.save()
if want_to_keep_b:
transaction.savepoint_commit(sid)
else:
transaction.savepoint_rollback(sid)
6. Controlling Rollback Behavior
If an error occurs inside an atomic() block, Django rolls back the entire block—even if you handled the error with a savepoint.
To control this behavior:
Force rollback:
transaction.set_rollback(True)
Prevent rollback:
transaction.set_rollback(False)
Warning: Only set rollback=False after rolling back to a known‑good savepoint. Otherwise you risk breaking atomicity and corrupting data.
7. Database-Specific Notes
7.1 Savepoints in SQLite
SQLite supports savepoints, but the Python sqlite3 module has limitations:
- Savepoints only work inside an
atomic()block. - When autocommit is off, SQLite performs implicit commits before savepoint statements.
- You cannot use
atomic()when autocommit is disabled.
7.2 Transactions in MySQL
MySQL supports transactions only with certain table engines:
- InnoDB → supports transactions
- MyISAM → does not support transactions
If transactions are unsupported, Django always runs in autocommit mode.
7.3 Handling Exceptions in PostgreSQL
In PostgreSQL, once an exception occurs inside a transaction, all subsequent queries fail with:
current transaction is aborted, queries ignored until end of transaction block
To recover, you must roll back.
Option 1: Full rollback
a.save()
try:
b.save()
except IntegrityError:
transaction.rollback()
c.save()
This undoes all uncommitted changes, including a.save().
Option 2: Savepoint rollback
a.save()
sid = transaction.savepoint()
try:
b.save()
transaction.savepoint_commit(sid)
except IntegrityError:
transaction.savepoint_rollback(sid)
c.save()
Here, a.save() is preserved even if b.save() fails.
Conclusion
Savepoints provide fine‑grained control over rollbacks inside transactions. Django manages them automatically in nested atomic() blocks, but also exposes low‑level APIs for advanced use cases. Understanding database‑specific behavior—especially in SQLite, MySQL, and PostgreSQL—is essential for using savepoints safely and effectively.
Written & researched by Dr. Shahin Siami