Transactions¶
MongoDB supports transactions if it’s configured as a replica set or a sharded cluster.
Because MongoDB transactions have some limitations and are not meant to be used
as freely as SQL transactions, Django’s transactions APIs, including most notably
django.db.transaction.atomic(), function as no-ops.
Instead, Django MongoDB Backend provides its own
django_mongodb_backend.transaction.atomic() function.
Outside of a transaction, query execution uses Django and MongoDB’s default behavior of autocommit mode. Each query is immediately committed to the database.
Controlling transactions¶
- atomic(using=None)¶
Atomicity is the defining property of database transactions.
atomicallows creating a block of code within which the atomicity on the database is guaranteed. If the block of code is successfully completed, the changes are committed to the database. If there is an exception, the changes are rolled back.atomicis usable both as a decorator:from django_mongodb_backend import transaction @transaction.atomic def viewfunc(request): # This code executes inside a transaction. do_stuff()
and as a context manager:
from django_mongodb_backend import transaction def viewfunc(request): # This code executes in autocommit mode (Django's default). do_stuff() with transaction.atomic(): # This code executes inside a transaction. do_more_stuff()
Avoid catching exceptions inside
atomic!When exiting an
atomicblock, Django looks at whether it’s exited normally or with an exception to determine whether to commit or roll back. If you catch and handle exceptions inside anatomicblock, you may hide from Django the fact that a problem has happened. This can result in unexpected behavior.This is mostly a concern for
DatabaseErrorand its subclasses such asIntegrityError. After such an error, the transaction is broken and Django will perform a rollback at the end of theatomicblock.You may need to manually revert app state when rolling back a transaction.
The values of a model’s fields won’t be reverted when a transaction rollback happens. This could lead to an inconsistent model state unless you manually restore the original field values.
For example, given
MyModelwith anactivefield, this snippet ensures that theif obj.activecheck at the end uses the correct value if updatingactivetoTruefails in the transaction:from django_mongodb_backend import transaction from django.db import DatabaseError obj = MyModel(active=False) obj.active = True try: with transaction.atomic(): obj.save() except DatabaseError: obj.active = False if obj.active: ...
This also applies to any other mechanism that may hold app state, such as caching or global variables. For example, if the code proactively updates data in the cache after saving an object, it’s recommended to use transaction.on_commit() instead, to defer cache alterations until the transaction is actually committed.
atomictakes ausingargument which should be the name of a database. If this argument isn’t provided, Django uses the"default"database.
Performance considerations
Open transactions have a performance cost for your MongoDB server. To
minimize this overhead, keep your transactions as short as possible. This
is especially important if you’re using atomic() in long-running
processes, outside of Django’s request / response cycle.
Performing actions after commit¶
The atomic() function supports Django’s
on_commit() API to perform actions after a
transaction successfully commits.
For convenience, on_commit() is aliased at
django_mongodb_backend.transaction.on_commit so you can use both:
from django_mongodb_backend import transaction
transaction.atomic()
transaction.on_commit(...)
Limitations¶
MongoDB’s transaction limitations that are applicable to Django are:
QuerySet.union()is not supported inside a transaction.Savepoints (i.e. nested
atomic()blocks) aren’t supported. The outermostatomic()will start a transaction while any inneratomic()blocks have no effect.