mirror of
https://github.com/django/django.git
synced 2024-12-23 17:46:27 +00:00
728548e483
Squashed commit of the following: commit 63ddb271a44df389b2c302e421fc17b7f0529755 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Sep 29 22:51:00 2013 +0200 Clarified interactions between atomic and exceptions. commit 2899ec299228217c876ba3aa4024e523a41c8504 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Sep 22 22:45:32 2013 +0200 Fixed TransactionManagementError in tests. Previous commit introduced an additional check to prevent running queries in transactions that will be rolled back, which triggered a few failures in the tests. In practice using transaction.atomic instead of the low-level savepoint APIs was enough to fix the problems. commit 4a639b059ea80aeb78f7f160a7d4b9f609b9c238 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Sep 24 22:24:17 2013 +0200 Allowed nesting constraint_checks_disabled inside atomic. Since MySQL handles transactions loosely, this isn't a problem. commit 2a4ab1cb6e83391ff7e25d08479e230ca564bfef Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sat Sep 21 18:43:12 2013 +0200 Prevented running queries in transactions that will be rolled back. This avoids a counter-intuitive behavior in an edge case on databases with non-atomic transaction semantics. It prevents using savepoint_rollback() inside an atomic block without calling set_rollback(False) first, which is backwards-incompatible in tests. Refs #21134. commit 8e3db393853c7ac64a445b66e57f3620a3fde7b0 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Sun Sep 22 22:14:17 2013 +0200 Replaced manual savepoints by atomic blocks. This ensures the rollback flag is handled consistently in internal APIs.
746 lines
28 KiB
Plaintext
746 lines
28 KiB
Plaintext
=====================
|
|
Database transactions
|
|
=====================
|
|
|
|
.. module:: django.db.transaction
|
|
|
|
Django gives you a few ways to control how database transactions are managed.
|
|
|
|
Managing database transactions
|
|
==============================
|
|
|
|
Django's default transaction behavior
|
|
-------------------------------------
|
|
|
|
Django's default behavior is to run in autocommit mode. Each query is
|
|
immediately committed to the database. :ref:`See below for details
|
|
<autocommit-details>`.
|
|
|
|
Django uses transactions or savepoints automatically to guarantee the
|
|
integrity of ORM operations that require multiple queries, especially
|
|
:ref:`delete() <topics-db-queries-delete>` and :ref:`update()
|
|
<topics-db-queries-update>` queries.
|
|
|
|
.. versionchanged:: 1.6
|
|
|
|
Previous version of Django featured :ref:`a more complicated default
|
|
behavior <transactions-upgrading-from-1.5>`.
|
|
|
|
.. _tying-transactions-to-http-requests:
|
|
|
|
Tying transactions to HTTP requests
|
|
-----------------------------------
|
|
|
|
A common way to handle transactions on the web is to wrap each request in a
|
|
transaction. Set :setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>` to
|
|
``True`` in the configuration of each database for which you want to enable
|
|
this behavior.
|
|
|
|
It works like this. Before calling a view function, Django starts a
|
|
transaction. If the response is produced without problems, Django commits the
|
|
transaction. If the view produces an exception, Django rolls back the
|
|
transaction.
|
|
|
|
You may perfom partial commits and rollbacks in your view code, typically with
|
|
the :func:`atomic` context manager. However, at the end of the view, either
|
|
all the changes will be committed, or none of them.
|
|
|
|
.. warning::
|
|
|
|
While the simplicity of this transaction model is appealing, it also makes it
|
|
inefficient when traffic increases. Opening a transaction for every view has
|
|
some overhead. The impact on performance depends on the query patterns of your
|
|
application and on how well your database handles locking.
|
|
|
|
.. admonition:: Per-request transactions and streaming responses
|
|
|
|
When a view returns a :class:`~django.http.StreamingHttpResponse`, reading
|
|
the contents of the response will often execute code to generate the
|
|
content. Since the view has already returned, such code runs outside of
|
|
the transaction.
|
|
|
|
Generally speaking, it isn't advisable to write to the database while
|
|
generating a streaming response, since there's no sensible way to handle
|
|
errors after starting to send the response.
|
|
|
|
In practice, this feature simply wraps every view function in the :func:`atomic`
|
|
decorator described below.
|
|
|
|
Note that only the execution of your view is enclosed in the transactions.
|
|
Middleware runs outside of the transaction, and so does the rendering of
|
|
template responses.
|
|
|
|
When :setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>` is enabled, it's
|
|
still possible to prevent views from running in a transaction.
|
|
|
|
.. function:: non_atomic_requests(using=None)
|
|
|
|
This decorator will negate the effect of :setting:`ATOMIC_REQUESTS
|
|
<DATABASE-ATOMIC_REQUESTS>` for a given view::
|
|
|
|
from django.db import transaction
|
|
|
|
@transaction.non_atomic_requests
|
|
def my_view(request):
|
|
do_stuff()
|
|
|
|
@transaction.non_atomic_requests(using='other')
|
|
def my_other_view(request):
|
|
do_stuff_on_the_other_database()
|
|
|
|
It only works if it's applied to the view itself.
|
|
|
|
.. versionchanged:: 1.6
|
|
|
|
Django used to provide this feature via ``TransactionMiddleware``, which is
|
|
now deprecated.
|
|
|
|
Controlling transactions explicitly
|
|
-----------------------------------
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
Django provides a single API to control database transactions.
|
|
|
|
.. function:: atomic(using=None, savepoint=True)
|
|
|
|
Atomicity is the defining property of database transactions. ``atomic``
|
|
allows us to create 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`` blocks can be nested. In this case, when an inner block
|
|
completes successfully, its effects can still be rolled back if an
|
|
exception is raised in the outer block at a later point.
|
|
|
|
``atomic`` is usable both as a `decorator`_::
|
|
|
|
from django.db import transaction
|
|
|
|
@transaction.atomic
|
|
def viewfunc(request):
|
|
# This code executes inside a transaction.
|
|
do_stuff()
|
|
|
|
and as a `context manager`_::
|
|
|
|
from django.db 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()
|
|
|
|
.. _decorator: http://docs.python.org/glossary.html#term-decorator
|
|
.. _context manager: http://docs.python.org/glossary.html#term-context-manager
|
|
|
|
Wrapping ``atomic`` in a try/except block allows for natural handling of
|
|
integrity errors::
|
|
|
|
from django.db import IntegrityError, transaction
|
|
|
|
@transaction.atomic
|
|
def viewfunc(request):
|
|
create_parent()
|
|
|
|
try:
|
|
with transaction.atomic():
|
|
generate_relationships()
|
|
except IntegrityError:
|
|
handle_exception()
|
|
|
|
add_children()
|
|
|
|
In this example, even if ``generate_relationships()`` causes a database
|
|
error by breaking an integrity constraint, you can execute queries in
|
|
``add_children()``, and the changes from ``create_parent()`` are still
|
|
there. Note that any operations attempted in ``generate_relationships()``
|
|
will already have been rolled back safely when ``handle_exception()`` is
|
|
called, so the exception handler can also operate on the database if
|
|
necessary.
|
|
|
|
.. 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. If you attempt to run database
|
|
queries before the rollback happens, Django will raise a
|
|
:class:`~django.db.transaction.TransactionManagementError`. You may
|
|
also encounter this behavior when an ORM-related signal handler raises
|
|
an exception.
|
|
|
|
The correct way to catch database errors is around an ``atomic`` block
|
|
as shown above. If necessary, add an extra ``atomic`` block for this
|
|
purpose. This pattern has another advantage: it delimits explicitly
|
|
which operations will be rolled back if an exception occurs.
|
|
|
|
If you catch exceptions raised by raw SQL queries, Django's behavior
|
|
is unspecified and database-dependent.
|
|
|
|
In order to guarantee atomicity, ``atomic`` disables some APIs. Attempting
|
|
to commit, roll back, or change the autocommit state of the database
|
|
connection within an ``atomic`` block will raise an exception.
|
|
|
|
``atomic`` takes a ``using`` argument which should be the name of a
|
|
database. If this argument isn't provided, Django uses the ``"default"``
|
|
database.
|
|
|
|
Under the hood, Django's transaction management code:
|
|
|
|
- opens a transaction when entering the outermost ``atomic`` block;
|
|
- creates a savepoint when entering an inner ``atomic`` block;
|
|
- releases or rolls back to the savepoint when exiting an inner block;
|
|
- commits or rolls back the transaction when exiting the outermost block.
|
|
|
|
You can disable the creation of savepoints for inner blocks by setting the
|
|
``savepoint`` argument to ``False``. If an exception occurs, Django will
|
|
perform the rollback when exiting the first parent block with a savepoint
|
|
if there is one, and the outermost block otherwise. Atomicity is still
|
|
guaranteed by the outer transaction. This option should only be used if
|
|
the overhead of savepoints is noticeable. It has the drawback of breaking
|
|
the error handling described above.
|
|
|
|
You may use ``atomic`` when autocommit is turned off. It will only use
|
|
savepoints, even for the outermost block, and it will raise an exception
|
|
if the outermost block is declared with ``savepoint=False``.
|
|
|
|
.. admonition:: Performance considerations
|
|
|
|
Open transactions have a performance cost for your database server. To
|
|
minimize this overhead, keep your transactions as short as possible. This
|
|
is especially important of you're using :func:`atomic` in long-running
|
|
processes, outside of Django's request / response cycle.
|
|
|
|
Autocommit
|
|
==========
|
|
|
|
.. _autocommit-details:
|
|
|
|
Why Django uses autocommit
|
|
--------------------------
|
|
|
|
In the SQL standards, each SQL query starts a transaction, unless one is
|
|
already in progress. Such transactions must then be committed or rolled back.
|
|
|
|
This isn't always convenient for application developers. To alleviate this
|
|
problem, most databases provide an autocommit mode. When autocommit is turned
|
|
on, each SQL query is wrapped in its own transaction. In other words, the
|
|
transaction is not only automatically started, but also automatically
|
|
committed.
|
|
|
|
:pep:`249`, the Python Database API Specification v2.0, requires autocommit to
|
|
be initially turned off. Django overrides this default and turns autocommit
|
|
on.
|
|
|
|
To avoid this, you can :ref:`deactivate the transaction management
|
|
<deactivate-transaction-management>`, but it isn't recommended.
|
|
|
|
.. versionchanged:: 1.6
|
|
|
|
Before Django 1.6, autocommit was turned off, and it was emulated by
|
|
forcing a commit after write operations in the ORM.
|
|
|
|
.. _deactivate-transaction-management:
|
|
|
|
Deactivating transaction management
|
|
-----------------------------------
|
|
|
|
You can totally disable Django's transaction management for a given database
|
|
by setting :setting:`AUTOCOMMIT <DATABASE-AUTOCOMMIT>` to ``False`` in its
|
|
configuration. If you do this, Django won't enable autocommit, and won't
|
|
perform any commits. You'll get the regular behavior of the underlying
|
|
database library.
|
|
|
|
This requires you to commit explicitly every transaction, even those started
|
|
by Django or by third-party libraries. Thus, this is best used in situations
|
|
where you want to run your own transaction-controlling middleware or do
|
|
something really strange.
|
|
|
|
.. versionchanged:: 1.6
|
|
|
|
This used to be controlled by the ``TRANSACTIONS_MANAGED`` setting.
|
|
|
|
Low-level APIs
|
|
==============
|
|
|
|
.. warning::
|
|
|
|
Always prefer :func:`atomic` if possible at all. It accounts for the
|
|
idiosyncrasies of each database and prevents invalid operations.
|
|
|
|
The low level APIs are only useful if you're implementing your own
|
|
transaction management.
|
|
|
|
.. _managing-autocommit:
|
|
|
|
Autocommit
|
|
----------
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
Django provides a straightforward API in the :mod:`django.db.transaction`
|
|
module to manage the autocommit state of each database connection.
|
|
|
|
.. function:: get_autocommit(using=None)
|
|
|
|
.. function:: set_autocommit(autocommit, using=None)
|
|
|
|
These functions take a ``using`` argument which should be the name of a
|
|
database. If it isn't provided, Django uses the ``"default"`` database.
|
|
|
|
Autocommit is initially turned on. If you turn it off, it's your
|
|
responsibility to restore it.
|
|
|
|
Once you turn autocommit off, you get the default behavior of your database
|
|
adapter, and Django won't help you. Although that behavior is specified in
|
|
:pep:`249`, implementations of adapters aren't always consistent with one
|
|
another. Review the documentation of the adapter you're using carefully.
|
|
|
|
You must ensure that no transaction is active, usually by issuing a
|
|
:func:`commit` or a :func:`rollback`, before turning autocommit back on.
|
|
|
|
Django will refuse to turn autocommit off when an :func:`atomic` block is
|
|
active, because that would break atomicity.
|
|
|
|
Transactions
|
|
------------
|
|
|
|
A transaction is an atomic set of database queries. Even if your program
|
|
crashes, the database guarantees that either all the changes will be applied,
|
|
or none of them.
|
|
|
|
Django doesn't provide an API to start a transaction. The expected way to
|
|
start a transaction is to disable autocommit with :func:`set_autocommit`.
|
|
|
|
Once you're in a transaction, you can choose either to apply the changes
|
|
you've performed until this point with :func:`commit`, or to cancel them with
|
|
:func:`rollback`. These functions are defined in :mod:`django.db.transaction`.
|
|
|
|
.. function:: commit(using=None)
|
|
|
|
.. function:: rollback(using=None)
|
|
|
|
These functions take a ``using`` argument which should be the name of a
|
|
database. If it isn't provided, Django uses the ``"default"`` database.
|
|
|
|
Django will refuse to commit or to rollback when an :func:`atomic` block is
|
|
active, because that would break atomicity.
|
|
|
|
.. _topics-db-transactions-savepoints:
|
|
|
|
Savepoints
|
|
----------
|
|
|
|
A savepoint is a marker within a transaction that enables you to roll back
|
|
part of a transaction, rather than the full transaction. Savepoints are
|
|
available with the SQLite (≥ 3.6.8), PostgreSQL, Oracle and MySQL (when using
|
|
the InnoDB storage engine) backends. Other backends provide the savepoint
|
|
functions, but they're empty operations -- they don't actually do anything.
|
|
|
|
Savepoints aren't especially useful if you are using autocommit, the default
|
|
behavior of Django. However, once you open a transaction with :func:`atomic`,
|
|
you build up a series of database operations awaiting a commit or rollback. If
|
|
you issue a rollback, the entire transaction is rolled back. Savepoints
|
|
provide the ability to perform a fine-grained rollback, rather than the full
|
|
rollback that would be performed by ``transaction.rollback()``.
|
|
|
|
.. versionchanged:: 1.6
|
|
|
|
When the :func:`atomic` decorator is nested, it creates a savepoint to allow
|
|
partial commit or rollback. You're strongly encouraged to use :func:`atomic`
|
|
rather than the functions described below, but they're still part of the
|
|
public API, and there's no plan to deprecate them.
|
|
|
|
Each of these functions takes a ``using`` argument which should be the name of
|
|
a database for which the behavior applies. If no ``using`` argument is
|
|
provided then the ``"default"`` database is used.
|
|
|
|
Savepoints are controlled by three functions in :mod:`django.db.transaction`:
|
|
|
|
.. function:: savepoint(using=None)
|
|
|
|
Creates a new savepoint. This marks a point in the transaction that is
|
|
known to be in a "good" state. Returns the savepoint ID (``sid``).
|
|
|
|
.. function:: savepoint_commit(sid, using=None)
|
|
|
|
Releases savepoint ``sid``. The changes performed since the savepoint was
|
|
created become part of the transaction.
|
|
|
|
.. function:: savepoint_rollback(sid, using=None)
|
|
|
|
Rolls back the transaction to savepoint ``sid``.
|
|
|
|
These functions do nothing if savepoints aren't supported or if the database
|
|
is in autocommit mode.
|
|
|
|
In addition, there's a utility function:
|
|
|
|
.. function:: clean_savepoints(using=None)
|
|
|
|
Resets the counter used to generate unique savepoint IDs.
|
|
|
|
The following example demonstrates the use of savepoints::
|
|
|
|
from django.db import transaction
|
|
|
|
# open a transaction
|
|
@transaction.atomic
|
|
def viewfunc(request):
|
|
|
|
a.save()
|
|
# transaction now contains a.save()
|
|
|
|
sid = transaction.savepoint()
|
|
|
|
b.save()
|
|
# transaction now contains a.save() and b.save()
|
|
|
|
if want_to_keep_b:
|
|
transaction.savepoint_commit(sid)
|
|
# open transaction still contains a.save() and b.save()
|
|
else:
|
|
transaction.savepoint_rollback(sid)
|
|
# open transaction now contains only a.save()
|
|
|
|
.. versionadded:: 1.6
|
|
|
|
Savepoints may be used to recover from a database error by performing a partial
|
|
rollback. If you're doing this inside an :func:`atomic` block, the entire block
|
|
will still be rolled back, because it doesn't know you've handled the situation
|
|
at a lower level! To prevent this, you can control the rollback behavior with
|
|
the following functions.
|
|
|
|
.. function:: get_rollback(using=None)
|
|
|
|
.. function:: set_rollback(rollback, using=None)
|
|
|
|
Setting the rollback flag to ``True`` forces a rollback when exiting the
|
|
innermost atomic block. This may be useful to trigger a rollback without
|
|
raising an exception.
|
|
|
|
Setting it to ``False`` prevents such a rollback. Before doing that, make sure
|
|
you've rolled back the transaction to a known-good savepoint within the current
|
|
atomic block! Otherwise you're breaking atomicity and data corruption may
|
|
occur.
|
|
|
|
Database-specific notes
|
|
=======================
|
|
|
|
.. _savepoints-in-sqlite:
|
|
|
|
Savepoints in SQLite
|
|
--------------------
|
|
|
|
While SQLite ≥ 3.6.8 supports savepoints, a flaw in the design of the
|
|
:mod:`sqlite3` module makes them hardly usable.
|
|
|
|
When autocommit is enabled, savepoints don't make sense. When it's disabled,
|
|
:mod:`sqlite3` commits implicitly before savepoint statements. (In fact, it
|
|
commits before any statement other than ``SELECT``, ``INSERT``, ``UPDATE``,
|
|
``DELETE`` and ``REPLACE``.) This bug has two consequences:
|
|
|
|
- The low level APIs for savepoints are only usable inside a transaction ie.
|
|
inside an :func:`atomic` block.
|
|
- It's impossible to use :func:`atomic` when autocommit is turned off.
|
|
|
|
Transactions in MySQL
|
|
---------------------
|
|
|
|
If you're using MySQL, your tables may or may not support transactions; it
|
|
depends on your MySQL version and the table types you're using. (By
|
|
"table types," we mean something like "InnoDB" or "MyISAM".) MySQL transaction
|
|
peculiarities are outside the scope of this article, but the MySQL site has
|
|
`information on MySQL transactions`_.
|
|
|
|
If your MySQL setup does *not* support transactions, then Django will always
|
|
function in autocommit mode: statements will be executed and committed as soon
|
|
as they're called. If your MySQL setup *does* support transactions, Django
|
|
will handle transactions as explained in this document.
|
|
|
|
.. _information on MySQL transactions: http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html
|
|
|
|
Handling exceptions within PostgreSQL transactions
|
|
--------------------------------------------------
|
|
|
|
.. note::
|
|
|
|
This section is relevant only if you're implementing your own transaction
|
|
management. This problem cannot occur in Django's default mode and
|
|
:func:`atomic` handles it automatically.
|
|
|
|
Inside a transaction, when a call to a PostgreSQL cursor raises an exception
|
|
(typically ``IntegrityError``), all subsequent SQL in the same transaction
|
|
will fail with the error "current transaction is aborted, queries ignored
|
|
until end of transaction block". Whilst simple use of ``save()`` is unlikely
|
|
to raise an exception in PostgreSQL, there are more advanced usage patterns
|
|
which might, such as saving objects with unique fields, saving using the
|
|
force_insert/force_update flag, or invoking custom SQL.
|
|
|
|
There are several ways to recover from this sort of error.
|
|
|
|
Transaction rollback
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The first option is to roll back the entire transaction. For example::
|
|
|
|
a.save() # Succeeds, but may be undone by transaction rollback
|
|
try:
|
|
b.save() # Could throw exception
|
|
except IntegrityError:
|
|
transaction.rollback()
|
|
c.save() # Succeeds, but a.save() may have been undone
|
|
|
|
Calling ``transaction.rollback()`` rolls back the entire transaction. Any
|
|
uncommitted database operations will be lost. In this example, the changes
|
|
made by ``a.save()`` would be lost, even though that operation raised no error
|
|
itself.
|
|
|
|
Savepoint rollback
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
You can use :ref:`savepoints <topics-db-transactions-savepoints>` to control
|
|
the extent of a rollback. Before performing a database operation that could
|
|
fail, you can set or update the savepoint; that way, if the operation fails,
|
|
you can roll back the single offending operation, rather than the entire
|
|
transaction. For example::
|
|
|
|
a.save() # Succeeds, and never undone by savepoint rollback
|
|
try:
|
|
sid = transaction.savepoint()
|
|
b.save() # Could throw exception
|
|
transaction.savepoint_commit(sid)
|
|
except IntegrityError:
|
|
transaction.savepoint_rollback(sid)
|
|
c.save() # Succeeds, and a.save() is never undone
|
|
|
|
In this example, ``a.save()`` will not be undone in the case where
|
|
``b.save()`` raises an exception.
|
|
|
|
.. _transactions-upgrading-from-1.5:
|
|
|
|
Changes from Django 1.5 and earlier
|
|
===================================
|
|
|
|
The features described below were deprecated in Django 1.6 and will be removed
|
|
in Django 1.8. They're documented in order to ease the migration to the new
|
|
transaction management APIs.
|
|
|
|
Legacy APIs
|
|
-----------
|
|
|
|
The following functions, defined in ``django.db.transaction``, provided a way
|
|
to control transactions on a per-function or per-code-block basis. They could
|
|
be used as decorators or as context managers, and they accepted a ``using``
|
|
argument, exactly like :func:`atomic`.
|
|
|
|
.. function:: autocommit
|
|
|
|
Enable Django's default autocommit behavior.
|
|
|
|
Transactions will be committed as soon as you call ``model.save()``,
|
|
``model.delete()``, or any other function that writes to the database.
|
|
|
|
.. function:: commit_on_success
|
|
|
|
Use a single transaction for all the work done in a function.
|
|
|
|
If the function returns successfully, then Django will commit all work done
|
|
within the function at that point. If the function raises an exception,
|
|
though, Django will roll back the transaction.
|
|
|
|
.. function:: commit_manually
|
|
|
|
Tells Django you'll be managing the transaction on your own.
|
|
|
|
Whether you are writing or simply reading from the database, you must
|
|
``commit()`` or ``rollback()`` explicitly or Django will raise a
|
|
:exc:`TransactionManagementError` exception. This is required when reading
|
|
from the database because ``SELECT`` statements may call functions which
|
|
modify tables, and thus it is impossible to know if any data has been
|
|
modified.
|
|
|
|
.. _transaction-states:
|
|
|
|
Transaction states
|
|
------------------
|
|
|
|
The three functions described above relied on a concept called "transaction
|
|
states". This mechanism was deprecated in Django 1.6, but it's still available
|
|
until Django 1.8.
|
|
|
|
At any time, each database connection is in one of these two states:
|
|
|
|
- **auto mode**: autocommit is enabled;
|
|
- **managed mode**: autocommit is disabled.
|
|
|
|
Django starts in auto mode. ``TransactionMiddleware``,
|
|
:func:`commit_on_success` and :func:`commit_manually` activate managed mode;
|
|
:func:`autocommit` activates auto mode.
|
|
|
|
Internally, Django keeps a stack of states. Activations and deactivations must
|
|
be balanced.
|
|
|
|
For example, :func:`commit_on_success` switches to managed mode when entering
|
|
the block of code it controls; when exiting the block, it commits or
|
|
rollbacks, and switches back to auto mode.
|
|
|
|
So :func:`commit_on_success` really has two effects: it changes the
|
|
transaction state and it defines an transaction block. Nesting will give the
|
|
expected results in terms of transaction state, but not in terms of
|
|
transaction semantics. Most often, the inner block will commit, breaking the
|
|
atomicity of the outer block.
|
|
|
|
:func:`autocommit` and :func:`commit_manually` have similar limitations.
|
|
|
|
API changes
|
|
-----------
|
|
|
|
Transaction middleware
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
In Django 1.6, ``TransactionMiddleware`` is deprecated and replaced by
|
|
:setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>`. While the general
|
|
behavior is the same, there are two differences.
|
|
|
|
With the previous API, it was possible to switch to autocommit or to commit
|
|
explicitly anywhere inside a view. Since :setting:`ATOMIC_REQUESTS
|
|
<DATABASE-ATOMIC_REQUESTS>` relies on :func:`atomic` which enforces atomicity,
|
|
this isn't allowed any longer. However, at the toplevel, it's still possible
|
|
to avoid wrapping an entire view in a transaction. To achieve this, decorate
|
|
the view with :func:`non_atomic_requests` instead of :func:`autocommit`.
|
|
|
|
The transaction middleware applied not only to view functions, but also to
|
|
middleware modules that came after it. For instance, if you used the session
|
|
middleware after the transaction middleware, session creation was part of the
|
|
transaction. :setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>` only
|
|
applies to the view itself.
|
|
|
|
Managing transactions
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Starting with Django 1.6, :func:`atomic` is the only supported API for
|
|
defining a transaction. Unlike the deprecated APIs, it's nestable and always
|
|
guarantees atomicity.
|
|
|
|
In most cases, it will be a drop-in replacement for :func:`commit_on_success`.
|
|
|
|
During the deprecation period, it's possible to use :func:`atomic` within
|
|
:func:`autocommit`, :func:`commit_on_success` or :func:`commit_manually`.
|
|
However, the reverse is forbidden, because nesting the old decorators /
|
|
context managers breaks atomicity.
|
|
|
|
Managing autocommit
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
Django 1.6 introduces an explicit :ref:`API for managing autocommit
|
|
<managing-autocommit>`.
|
|
|
|
To disable autocommit temporarily, instead of::
|
|
|
|
with transaction.commit_manually():
|
|
# do stuff
|
|
|
|
you should now use::
|
|
|
|
transaction.set_autocommit(False)
|
|
try:
|
|
# do stuff
|
|
finally:
|
|
transaction.set_autocommit(True)
|
|
|
|
To enable autocommit temporarily, instead of::
|
|
|
|
with transaction.autocommit():
|
|
# do stuff
|
|
|
|
you should now use::
|
|
|
|
transaction.set_autocommit(True)
|
|
try:
|
|
# do stuff
|
|
finally:
|
|
transaction.set_autocommit(False)
|
|
|
|
Unless you're implementing a transaction management framework, you shouldn't
|
|
ever need to do this.
|
|
|
|
Disabling transaction management
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Instead of setting ``TRANSACTIONS_MANAGED = True``, set the ``AUTOCOMMIT`` key
|
|
to ``False`` in the configuration of each database, as explained in
|
|
:ref:`deactivate-transaction-management`.
|
|
|
|
Backwards incompatibilities
|
|
---------------------------
|
|
|
|
Since version 1.6, Django uses database-level autocommit in auto mode.
|
|
Previously, it implemented application-level autocommit by triggering a commit
|
|
after each ORM write.
|
|
|
|
As a consequence, each database query (for instance, an ORM read) started a
|
|
transaction that lasted until the next ORM write. Such "automatic
|
|
transactions" no longer exist in Django 1.6.
|
|
|
|
There are four known scenarios where this is backwards-incompatible.
|
|
|
|
Note that managed mode isn't affected at all. This section assumes auto mode.
|
|
See the :ref:`description of modes <transaction-states>` above.
|
|
|
|
Sequences of custom SQL queries
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you're executing several :ref:`custom SQL queries <executing-custom-sql>`
|
|
in a row, each one now runs in its own transaction, instead of sharing the
|
|
same "automatic transaction". If you need to enforce atomicity, you must wrap
|
|
the sequence of queries in :func:`atomic`.
|
|
|
|
To check for this problem, look for calls to ``cursor.execute()``. They're
|
|
usually followed by a call to ``transaction.commit_unless_managed()``, which
|
|
isn't useful any more and should be removed.
|
|
|
|
Select for update
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
If you were relying on "automatic transactions" to provide locking between
|
|
:meth:`~django.db.models.query.QuerySet.select_for_update` and a subsequent
|
|
write operation — an extremely fragile design, but nonetheless possible — you
|
|
must wrap the relevant code in :func:`atomic`.
|
|
|
|
Using a high isolation level
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you were using the "repeatable read" isolation level or higher, and if you
|
|
relied on "automatic transactions" to guarantee consistency between successive
|
|
reads, the new behavior might be backwards-incompatible. To enforce
|
|
consistency, you must wrap such sequences in :func:`atomic`.
|
|
|
|
MySQL defaults to "repeatable read" and SQLite to "serializable"; they may be
|
|
affected by this problem.
|
|
|
|
At the "read committed" isolation level or lower, "automatic transactions"
|
|
have no effect on the semantics of any sequence of ORM operations.
|
|
|
|
PostgreSQL and Oracle default to "read committed" and aren't affected, unless
|
|
you changed the isolation level.
|
|
|
|
Using unsupported database features
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
With triggers, views, or functions, it's possible to make ORM reads result in
|
|
database modifications. Django 1.5 and earlier doesn't deal with this case and
|
|
it's theoretically possible to observe a different behavior after upgrading to
|
|
Django 1.6 or later. In doubt, use :func:`atomic` to enforce integrity.
|