Changed API to disable ATOMIC_REQUESTS per view.
A decorator is easier to apply to CBVs. Backwards compatibility isn't an issue here, except for people running on a recent clone of master. Fixed a few minor problems in the transactions docs while I was there.
This commit is contained in:
parent
bdde7feb26
commit
6633eeb886
|
@ -66,10 +66,11 @@ class BaseHandler(object):
|
||||||
self._request_middleware = request_middleware
|
self._request_middleware = request_middleware
|
||||||
|
|
||||||
def make_view_atomic(self, view):
|
def make_view_atomic(self, view):
|
||||||
if getattr(view, 'transactions_per_request', True):
|
non_atomic_requests = getattr(view, '_non_atomic_requests', set())
|
||||||
for db in connections.all():
|
for db in connections.all():
|
||||||
if db.settings_dict['ATOMIC_REQUESTS']:
|
if (db.settings_dict['ATOMIC_REQUESTS']
|
||||||
view = transaction.atomic(using=db.alias)(view)
|
and db.alias not in non_atomic_requests):
|
||||||
|
view = transaction.atomic(using=db.alias)(view)
|
||||||
return view
|
return view
|
||||||
|
|
||||||
def get_response(self, request):
|
def get_response(self, request):
|
||||||
|
|
|
@ -333,6 +333,23 @@ def atomic(using=None, savepoint=True):
|
||||||
return Atomic(using, savepoint)
|
return Atomic(using, savepoint)
|
||||||
|
|
||||||
|
|
||||||
|
def _non_atomic_requests(view, using):
|
||||||
|
try:
|
||||||
|
view._non_atomic_requests.add(using)
|
||||||
|
except AttributeError:
|
||||||
|
view._non_atomic_requests = set([using])
|
||||||
|
return view
|
||||||
|
|
||||||
|
|
||||||
|
def non_atomic_requests(using=None):
|
||||||
|
if callable(using):
|
||||||
|
return _non_atomic_requests(using, DEFAULT_DB_ALIAS)
|
||||||
|
else:
|
||||||
|
if using is None:
|
||||||
|
using = DEFAULT_DB_ALIAS
|
||||||
|
return lambda view: _non_atomic_requests(view, using)
|
||||||
|
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# Deprecated decorators / context managers #
|
# Deprecated decorators / context managers #
|
||||||
############################################
|
############################################
|
||||||
|
|
|
@ -45,14 +45,6 @@ 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
|
the :func:`atomic` context manager. However, at the end of the view, either
|
||||||
all the changes will be committed, or none of them.
|
all the changes will be committed, or none of them.
|
||||||
|
|
||||||
To disable this behavior for a specific view, you must set the
|
|
||||||
``transactions_per_request`` attribute of the view function itself to
|
|
||||||
``False``, like this::
|
|
||||||
|
|
||||||
def my_view(request):
|
|
||||||
do_stuff()
|
|
||||||
my_view.transactions_per_request = False
|
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
While the simplicity of this transaction model is appealing, it also makes it
|
While the simplicity of this transaction model is appealing, it also makes it
|
||||||
|
@ -78,6 +70,26 @@ 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
|
Middleware runs outside of the transaction, and so does the rendering of
|
||||||
template responses.
|
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
|
.. versionchanged:: 1.6
|
||||||
|
|
||||||
Django used to provide this feature via ``TransactionMiddleware``, which is
|
Django used to provide this feature via ``TransactionMiddleware``, which is
|
||||||
|
@ -519,8 +531,8 @@ Transaction states
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The three functions described above relied on a concept called "transaction
|
The three functions described above relied on a concept called "transaction
|
||||||
states". This mechanisme was deprecated in Django 1.6, but it's still
|
states". This mechanism was deprecated in Django 1.6, but it's still available
|
||||||
available until Django 1.8.
|
until Django 1.8.
|
||||||
|
|
||||||
At any time, each database connection is in one of these two states:
|
At any time, each database connection is in one of these two states:
|
||||||
|
|
||||||
|
@ -554,23 +566,14 @@ Transaction middleware
|
||||||
|
|
||||||
In Django 1.6, ``TransactionMiddleware`` is deprecated and replaced
|
In Django 1.6, ``TransactionMiddleware`` is deprecated and replaced
|
||||||
:setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>`. While the general
|
:setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>`. While the general
|
||||||
behavior is the same, there are a few differences.
|
behavior is the same, there are two differences.
|
||||||
|
|
||||||
With the transaction middleware, it was still possible to switch to autocommit
|
With the previous API, it was possible to switch to autocommit or to commit
|
||||||
or to commit explicitly in a view. Since :func:`atomic` guarantees atomicity,
|
explicitly anywhere inside a view. Since :setting:`ATOMIC_REQUESTS
|
||||||
this isn't allowed any longer.
|
<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 a particular view in a transaction, instead of::
|
to avoid wrapping an entire view in a transaction. To achieve this, decorate
|
||||||
|
the view with :func:`non_atomic_requests` instead of :func:`autocommit`.
|
||||||
@transaction.autocommit
|
|
||||||
def my_view(request):
|
|
||||||
do_stuff()
|
|
||||||
|
|
||||||
you must now use this pattern::
|
|
||||||
|
|
||||||
def my_view(request):
|
|
||||||
do_stuff()
|
|
||||||
my_view.transactions_per_request = False
|
|
||||||
|
|
||||||
The transaction middleware applied not only to view functions, but also to
|
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 modules that came after it. For instance, if you used the session
|
||||||
|
@ -624,6 +627,9 @@ you should now use::
|
||||||
finally:
|
finally:
|
||||||
transaction.set_autocommit(False)
|
transaction.set_autocommit(False)
|
||||||
|
|
||||||
|
Unless you're implementing a transaction management framework, you shouldn't
|
||||||
|
ever need to do this.
|
||||||
|
|
||||||
Disabling transaction management
|
Disabling transaction management
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -653,7 +659,7 @@ Sequences of custom SQL queries
|
||||||
If you're executing several :ref:`custom SQL queries <executing-custom-sql>`
|
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
|
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
|
same "automatic transaction". If you need to enforce atomicity, you must wrap
|
||||||
the sequence of queries in :func:`commit_on_success`.
|
the sequence of queries in :func:`atomic`.
|
||||||
|
|
||||||
To check for this problem, look for calls to ``cursor.execute()``. They're
|
To check for this problem, look for calls to ``cursor.execute()``. They're
|
||||||
usually followed by a call to ``transaction.commit_unless_managed()``, which
|
usually followed by a call to ``transaction.commit_unless_managed()``, which
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import connection
|
from django.db import connection, transaction
|
||||||
from django.http import HttpResponse, StreamingHttpResponse
|
from django.http import HttpResponse, StreamingHttpResponse
|
||||||
|
|
||||||
def regular(request):
|
def regular(request):
|
||||||
|
@ -12,6 +12,6 @@ def streaming(request):
|
||||||
def in_transaction(request):
|
def in_transaction(request):
|
||||||
return HttpResponse(str(connection.in_atomic_block))
|
return HttpResponse(str(connection.in_atomic_block))
|
||||||
|
|
||||||
|
@transaction.non_atomic_requests
|
||||||
def not_in_transaction(request):
|
def not_in_transaction(request):
|
||||||
return HttpResponse(str(connection.in_atomic_block))
|
return HttpResponse(str(connection.in_atomic_block))
|
||||||
not_in_transaction.transactions_per_request = False
|
|
||||||
|
|
Loading…
Reference in New Issue