|  | 
|  | 1 | +============ | 
|  | 2 | +Transactions | 
|  | 3 | +============ | 
|  | 4 | + | 
|  | 5 | +.. versionadded:: 5.2.0b2 | 
|  | 6 | + | 
|  | 7 | +.. module:: django_mongodb_backend.transaction | 
|  | 8 | + | 
|  | 9 | +MongoDB supports :doc:`transactions <manual:core/transactions>` if it's | 
|  | 10 | +configured as a :doc:`replica set <manual:replication>` or a :doc:`sharded | 
|  | 11 | +cluster <manual:sharding>`. | 
|  | 12 | + | 
|  | 13 | +Because MongoDB transactions have some limitations and are not meant to be used | 
|  | 14 | +as freely as SQL transactions, :doc:`Django's transactions APIs | 
|  | 15 | +<django:topics/db/transactions>`, including most notably | 
|  | 16 | +:func:`django.db.transaction.atomic`, function as no-ops. | 
|  | 17 | + | 
|  | 18 | +Instead, Django MongoDB Backend provides its own | 
|  | 19 | +:func:`django_mongodb_backend.transaction.atomic` function. | 
|  | 20 | + | 
|  | 21 | +Outside of a transaction, query execution uses Django and MongoDB's default | 
|  | 22 | +behavior of autocommit mode. Each query is immediately committed to the | 
|  | 23 | +database. | 
|  | 24 | + | 
|  | 25 | +Controlling transactions | 
|  | 26 | +======================== | 
|  | 27 | + | 
|  | 28 | +.. function:: atomic(using=None) | 
|  | 29 | + | 
|  | 30 | + Atomicity is the defining property of database transactions. ``atomic`` | 
|  | 31 | + allows creating a block of code within which the atomicity on the database | 
|  | 32 | + is guaranteed. If the block of code is successfully completed, the changes | 
|  | 33 | + are committed to the database. If there is an exception, the changes are | 
|  | 34 | + rolled back. | 
|  | 35 | + | 
|  | 36 | + ``atomic`` is usable both as a :py:term:`decorator`:: | 
|  | 37 | + | 
|  | 38 | + from django_mongodb_backend import transaction | 
|  | 39 | + | 
|  | 40 | + | 
|  | 41 | + @transaction.atomic | 
|  | 42 | + def viewfunc(request): | 
|  | 43 | + # This code executes inside a transaction. | 
|  | 44 | + do_stuff() | 
|  | 45 | + | 
|  | 46 | + and as a :py:term:`context manager`:: | 
|  | 47 | + | 
|  | 48 | + from django_mongodb_backend import transaction | 
|  | 49 | + | 
|  | 50 | + | 
|  | 51 | + def viewfunc(request): | 
|  | 52 | + # This code executes in autocommit mode (Django's default). | 
|  | 53 | + do_stuff() | 
|  | 54 | + | 
|  | 55 | + with transaction.atomic(): | 
|  | 56 | + # This code executes inside a transaction. | 
|  | 57 | + do_more_stuff() | 
|  | 58 | + | 
|  | 59 | + .. admonition:: Avoid catching exceptions inside ``atomic``! | 
|  | 60 | + | 
|  | 61 | + When exiting an ``atomic`` block, Django looks at whether it's exited | 
|  | 62 | + normally or with an exception to determine whether to commit or roll | 
|  | 63 | + back. If you catch and handle exceptions inside an ``atomic`` block, | 
|  | 64 | + you may hide from Django the fact that a problem has happened. This can | 
|  | 65 | + result in unexpected behavior. | 
|  | 66 | + | 
|  | 67 | + This is mostly a concern for :exc:`~django.db.DatabaseError` and its | 
|  | 68 | + subclasses such as :exc:`~django.db.IntegrityError`. After such an | 
|  | 69 | + error, the transaction is broken and Django will perform a rollback at | 
|  | 70 | + the end of the ``atomic`` block. | 
|  | 71 | + | 
|  | 72 | + .. admonition:: You may need to manually revert app state when rolling back a transaction. | 
|  | 73 | + | 
|  | 74 | + The values of a model's fields won't be reverted when a transaction | 
|  | 75 | + rollback happens. This could lead to an inconsistent model state unless | 
|  | 76 | + you manually restore the original field values. | 
|  | 77 | + | 
|  | 78 | + For example, given ``MyModel`` with an ``active`` field, this snippet | 
|  | 79 | + ensures that the ``if obj.active`` check at the end uses the correct | 
|  | 80 | + value if updating ``active`` to ``True`` fails in the transaction:: | 
|  | 81 | + | 
|  | 82 | + from django_mongodb_backend import transaction | 
|  | 83 | + from django.db import DatabaseError | 
|  | 84 | + | 
|  | 85 | + obj = MyModel(active=False) | 
|  | 86 | + obj.active = True | 
|  | 87 | + try: | 
|  | 88 | + with transaction.atomic(): | 
|  | 89 | + obj.save() | 
|  | 90 | + except DatabaseError: | 
|  | 91 | + obj.active = False | 
|  | 92 | + | 
|  | 93 | + if obj.active: | 
|  | 94 | + ... | 
|  | 95 | + | 
|  | 96 | + This also applies to any other mechanism that may hold app state, such | 
|  | 97 | + as caching or global variables. For example, if the code proactively | 
|  | 98 | + updates data in the cache after saving an object, it's recommended to | 
|  | 99 | + use :ref:`transaction.on_commit() <performing-actions-after-commit>` | 
|  | 100 | + instead, to defer cache alterations until the transaction is actually | 
|  | 101 | + committed. | 
|  | 102 | + | 
|  | 103 | + ``atomic`` takes a ``using`` argument which should be the name of a | 
|  | 104 | + database. If this argument isn't provided, Django uses the ``"default"`` | 
|  | 105 | + database. | 
|  | 106 | + | 
|  | 107 | +.. admonition:: Performance considerations | 
|  | 108 | + | 
|  | 109 | + Open transactions have a performance cost for your MongoDB server. To | 
|  | 110 | + minimize this overhead, keep your transactions as short as possible. This | 
|  | 111 | + is especially important if you're using :func:`atomic` in long-running | 
|  | 112 | + processes, outside of Django's request / response cycle. | 
|  | 113 | + | 
|  | 114 | +Performing actions after commit | 
|  | 115 | +=============================== | 
|  | 116 | + | 
|  | 117 | +The :func:`atomic` function supports Django's | 
|  | 118 | +:func:`~django.db.transaction.on_commit` API to :ref:`perform actions after a | 
|  | 119 | +transaction successfully commits <performing-actions-after-commit>`. | 
|  | 120 | + | 
|  | 121 | +For convenience, :func:`~django.db.transaction.on_commit` is aliased at | 
|  | 122 | +``django_mongodb_backend.transaction.on_commit`` so you can use both:: | 
|  | 123 | + | 
|  | 124 | + from django_mongodb_backend import transaction | 
|  | 125 | + | 
|  | 126 | + | 
|  | 127 | + transaction.atomic() | 
|  | 128 | + transaction.on_commit(...) | 
|  | 129 | + | 
|  | 130 | +.. _transactions-limitations: | 
|  | 131 | + | 
|  | 132 | +Limitations | 
|  | 133 | +=========== | 
|  | 134 | + | 
|  | 135 | +MongoDB's transaction limitations that are applicable to Django are: | 
|  | 136 | + | 
|  | 137 | +- :meth:`QuerySet.union() <django.db.models.query.QuerySet.union>` is not | 
|  | 138 | + supported inside a transaction. | 
|  | 139 | +- Savepoints (i.e. nested :func:`~django.db.transaction.atomic` blocks) aren't | 
|  | 140 | + supported. The outermost :func:`~django.db.transaction.atomic` will start | 
|  | 141 | + a transaction while any inner :func:`~django.db.transaction.atomic` blocks | 
|  | 142 | + have no effect. | 
0 commit comments