Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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.
  • Loading branch information...
commit 6633eeb88634aa8c03c8737d2d23c96b96a37e0d 1 parent bdde7fe
@aaugustin aaugustin authored
View
9 django/core/handlers/base.py
@@ -66,10 +66,11 @@ def load_middleware(self):
self._request_middleware = request_middleware
def make_view_atomic(self, view):
- if getattr(view, 'transactions_per_request', True):
- for db in connections.all():
- if db.settings_dict['ATOMIC_REQUESTS']:
- view = transaction.atomic(using=db.alias)(view)
+ non_atomic_requests = getattr(view, '_non_atomic_requests', set())
+ for db in connections.all():
+ if (db.settings_dict['ATOMIC_REQUESTS']
+ and db.alias not in non_atomic_requests):
+ view = transaction.atomic(using=db.alias)(view)
return view
def get_response(self, request):
View
17 django/db/transaction.py
@@ -333,6 +333,23 @@ def atomic(using=None, savepoint=True):
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 #
############################################
View
60 docs/topics/db/transactions.txt
@@ -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
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::
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
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
@@ -519,8 +531,8 @@ Transaction states
------------------
The three functions described above relied on a concept called "transaction
-states". This mechanisme was deprecated in Django 1.6, but it's still
-available until Django 1.8.
+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:
@@ -554,23 +566,14 @@ Transaction middleware
In Django 1.6, ``TransactionMiddleware`` is deprecated and replaced
:setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>`. While the general
-behavior is the same, there are a few differences.
-
-With the transaction middleware, it was still possible to switch to autocommit
-or to commit explicitly in a view. Since :func:`atomic` guarantees atomicity,
-this isn't allowed any longer.
+behavior is the same, there are two differences.
-To avoid wrapping a particular view in a transaction, instead of::
-
- @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
+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
@@ -624,6 +627,9 @@ you should now use::
finally:
transaction.set_autocommit(False)
+Unless you're implementing a transaction management framework, you shouldn't
+ever need to do this.
+
Disabling transaction management
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -653,7 +659,7 @@ 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:`commit_on_success`.
+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
View
4 tests/handlers/views.py
@@ -1,6 +1,6 @@
from __future__ import unicode_literals
-from django.db import connection
+from django.db import connection, transaction
from django.http import HttpResponse, StreamingHttpResponse
def regular(request):
@@ -12,6 +12,6 @@ def streaming(request):
def in_transaction(request):
return HttpResponse(str(connection.in_atomic_block))
+@transaction.non_atomic_requests
def not_in_transaction(request):
return HttpResponse(str(connection.in_atomic_block))
-not_in_transaction.transactions_per_request = False

0 comments on commit 6633eeb

Please sign in to comment.
Something went wrong with that request. Please try again.