============ Transactions ============ .. module:: django_mongodb_backend.transaction MongoDB supports :doc:`transactions ` if it's configured as a :doc:`replica set ` or a :doc:`sharded cluster `. Because MongoDB transactions have some limitations and are not meant to be used as freely as SQL transactions, :doc:`Django's transactions APIs `, including most notably :func:`django.db.transaction.atomic`, function as no-ops. Instead, Django MongoDB Backend provides its own :func:`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 ======================== .. function:: atomic(using=None) Atomicity is the defining property of database transactions. ``atomic`` allows 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. ``atomic`` is usable both as a :py:term:`decorator`:: from django_mongodb_backend import transaction @transaction.atomic def viewfunc(request): # This code executes inside a transaction. do_stuff() and as a :py:term:`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() .. admonition:: Avoid catching exceptions inside ``atomic``! When exiting an ``atomic`` block, 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 an ``atomic`` block, you may hide from Django the fact that a problem has happened. This can result in unexpected behavior. This is mostly a concern for :exc:`~django.db.DatabaseError` and its subclasses such as :exc:`~django.db.IntegrityError`. After such an error, the transaction is broken and Django will perform a rollback at the end of the ``atomic`` block. .. admonition:: 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 ``MyModel`` with an ``active`` field, this snippet ensures that the ``if obj.active`` check at the end uses the correct value if updating ``active`` to ``True`` fails 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 :ref:`transaction.on_commit() ` instead, to defer cache alterations until the transaction is actually committed. ``atomic`` takes a ``using`` argument which should be the name of a database. If this argument isn't provided, Django uses the ``"default"`` database. .. admonition:: 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 :func:`atomic` in long-running processes, outside of Django's request / response cycle. Performing actions after commit =============================== The :func:`atomic` function supports Django's :func:`~django.db.transaction.on_commit` API to :ref:`perform actions after a transaction successfully commits `. For convenience, :func:`~django.db.transaction.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(...) .. _transactions-limitations: Limitations =========== MongoDB's transaction limitations that are applicable to Django are: - :meth:`QuerySet.union() ` is not supported inside a transaction. - Savepoints (i.e. nested :func:`~django.db.transaction.atomic` blocks) aren't supported. The outermost :func:`~django.db.transaction.atomic` will start a transaction while any inner :func:`~django.db.transaction.atomic` blocks have no effect.