Deprecated TransactionMiddleware and TRANSACTIONS_MANAGED.
Replaced them with per-database options, for proper multi-db support. Also toned down the recommendation to tie transactions to HTTP requests. Thanks Jeremy for sharing his experience.
This commit is contained in:
parent
f7245b83bb
commit
ac37ed21b3
|
@ -6,10 +6,10 @@ import types
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import exceptions
|
|
||||||
from django.core import urlresolvers
|
from django.core import urlresolvers
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
from django.core.exceptions import MiddlewareNotUsed, PermissionDenied
|
from django.core.exceptions import MiddlewareNotUsed, PermissionDenied
|
||||||
|
from django.db import connections, transaction
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.module_loading import import_by_path
|
from django.utils.module_loading import import_by_path
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -65,6 +65,13 @@ class BaseHandler(object):
|
||||||
# as a flag for initialization being complete.
|
# as a flag for initialization being complete.
|
||||||
self._request_middleware = request_middleware
|
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)
|
||||||
|
return view
|
||||||
|
|
||||||
def get_response(self, request):
|
def get_response(self, request):
|
||||||
"Returns an HttpResponse object for the given HttpRequest"
|
"Returns an HttpResponse object for the given HttpRequest"
|
||||||
try:
|
try:
|
||||||
|
@ -101,8 +108,9 @@ class BaseHandler(object):
|
||||||
break
|
break
|
||||||
|
|
||||||
if response is None:
|
if response is None:
|
||||||
|
wrapped_callback = self.make_view_atomic(callback)
|
||||||
try:
|
try:
|
||||||
response = callback(request, *callback_args, **callback_kwargs)
|
response = wrapped_callback(request, *callback_args, **callback_kwargs)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# If the view raised an exception, run it through exception
|
# If the view raised an exception, run it through exception
|
||||||
# middleware, and if the exception middleware returns a
|
# middleware, and if the exception middleware returns a
|
||||||
|
|
|
@ -104,7 +104,7 @@ class BaseDatabaseWrapper(object):
|
||||||
conn_params = self.get_connection_params()
|
conn_params = self.get_connection_params()
|
||||||
self.connection = self.get_new_connection(conn_params)
|
self.connection = self.get_new_connection(conn_params)
|
||||||
self.init_connection_state()
|
self.init_connection_state()
|
||||||
if not settings.TRANSACTIONS_MANAGED:
|
if self.settings_dict['AUTOCOMMIT']:
|
||||||
self.set_autocommit()
|
self.set_autocommit()
|
||||||
connection_created.send(sender=self.__class__, connection=self)
|
connection_created.send(sender=self.__class__, connection=self)
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ class BaseDatabaseWrapper(object):
|
||||||
if self.transaction_state:
|
if self.transaction_state:
|
||||||
managed = self.transaction_state[-1]
|
managed = self.transaction_state[-1]
|
||||||
else:
|
else:
|
||||||
managed = settings.TRANSACTIONS_MANAGED
|
managed = not self.settings_dict['AUTOCOMMIT']
|
||||||
|
|
||||||
if self._dirty:
|
if self._dirty:
|
||||||
self.rollback()
|
self.rollback()
|
||||||
|
|
|
@ -2,6 +2,7 @@ from functools import wraps
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
from threading import local
|
from threading import local
|
||||||
|
import warnings
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
@ -158,6 +159,13 @@ class ConnectionHandler(object):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ConnectionDoesNotExist("The connection %s doesn't exist" % alias)
|
raise ConnectionDoesNotExist("The connection %s doesn't exist" % alias)
|
||||||
|
|
||||||
|
conn.setdefault('ATOMIC_REQUESTS', False)
|
||||||
|
if settings.TRANSACTIONS_MANAGED:
|
||||||
|
warnings.warn(
|
||||||
|
"TRANSACTIONS_MANAGED is deprecated. Use AUTOCOMMIT instead.",
|
||||||
|
PendingDeprecationWarning, stacklevel=2)
|
||||||
|
conn.setdefault('AUTOCOMMIT', False)
|
||||||
|
conn.setdefault('AUTOCOMMIT', True)
|
||||||
conn.setdefault('ENGINE', 'django.db.backends.dummy')
|
conn.setdefault('ENGINE', 'django.db.backends.dummy')
|
||||||
if conn['ENGINE'] == 'django.db.backends.' or not conn['ENGINE']:
|
if conn['ENGINE'] == 'django.db.backends.' or not conn['ENGINE']:
|
||||||
conn['ENGINE'] = 'django.db.backends.dummy'
|
conn['ENGINE'] = 'django.db.backends.dummy'
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
from django.db import transaction
|
import warnings
|
||||||
|
|
||||||
|
from django.core.exceptions import MiddlewareNotUsed
|
||||||
|
from django.db import connection, transaction
|
||||||
|
|
||||||
class TransactionMiddleware(object):
|
class TransactionMiddleware(object):
|
||||||
"""
|
"""
|
||||||
|
@ -7,6 +10,14 @@ class TransactionMiddleware(object):
|
||||||
commit, the commit is done when a successful response is created. If an
|
commit, the commit is done when a successful response is created. If an
|
||||||
exception happens, the database is rolled back.
|
exception happens, the database is rolled back.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
warnings.warn(
|
||||||
|
"TransactionMiddleware is deprecated in favor of ATOMIC_REQUESTS.",
|
||||||
|
PendingDeprecationWarning, stacklevel=2)
|
||||||
|
if connection.settings_dict['ATOMIC_REQUESTS']:
|
||||||
|
raise MiddlewareNotUsed
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
"""Enters transaction management"""
|
"""Enters transaction management"""
|
||||||
transaction.enter_transaction_management()
|
transaction.enter_transaction_management()
|
||||||
|
|
|
@ -329,9 +329,14 @@ these changes.
|
||||||
1.8
|
1.8
|
||||||
---
|
---
|
||||||
|
|
||||||
* The decorators and context managers ``django.db.transaction.autocommit``,
|
* The following transaction management APIs will be removed:
|
||||||
``commit_on_success`` and ``commit_manually`` will be removed. See
|
|
||||||
:ref:`transactions-upgrading-from-1.5`.
|
- ``TransactionMiddleware``,
|
||||||
|
- the decorators and context managers ``autocommit``, ``commit_on_success``,
|
||||||
|
and ``commit_manually``,
|
||||||
|
- the ``TRANSACTIONS_MANAGED`` setting.
|
||||||
|
|
||||||
|
Upgrade paths are described in :ref:`transactions-upgrading-from-1.5`.
|
||||||
|
|
||||||
* The :ttag:`cycle` and :ttag:`firstof` template tags will auto-escape their
|
* The :ttag:`cycle` and :ttag:`firstof` template tags will auto-escape their
|
||||||
arguments. In 1.6 and 1.7, this behavior is provided by the version of these
|
arguments. In 1.6 and 1.7, this behavior is provided by the version of these
|
||||||
|
|
|
@ -205,6 +205,10 @@ Transaction middleware
|
||||||
|
|
||||||
.. class:: TransactionMiddleware
|
.. class:: TransactionMiddleware
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
``TransactionMiddleware`` is deprecated. The documentation of transactions
|
||||||
|
contains :ref:`upgrade instructions <transactions-upgrading-from-1.5>`.
|
||||||
|
|
||||||
Binds commit and rollback of the default database to the request/response
|
Binds commit and rollback of the default database to the request/response
|
||||||
phase. If a view function runs successfully, a commit is done. If it fails with
|
phase. If a view function runs successfully, a commit is done. If it fails with
|
||||||
an exception, a rollback is done.
|
an exception, a rollback is done.
|
||||||
|
|
|
@ -408,6 +408,30 @@ SQLite. This can be configured using the following::
|
||||||
For other database backends, or more complex SQLite configurations, other options
|
For other database backends, or more complex SQLite configurations, other options
|
||||||
will be required. The following inner options are available.
|
will be required. The following inner options are available.
|
||||||
|
|
||||||
|
.. setting:: DATABASE-ATOMIC_REQUESTS
|
||||||
|
|
||||||
|
ATOMIC_REQUESTS
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
Set this to ``True`` to wrap each HTTP request in a transaction on this
|
||||||
|
database. See :ref:`tying-transactions-to-http-requests`.
|
||||||
|
|
||||||
|
.. setting:: DATABASE-AUTOCOMMIT
|
||||||
|
|
||||||
|
AUTOCOMMIT
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 1.6
|
||||||
|
|
||||||
|
Default: ``True``
|
||||||
|
|
||||||
|
Set this to ``False`` if you want to :ref:`disable Django's transaction
|
||||||
|
management <deactivate-transaction-management>` and implement your own.
|
||||||
|
|
||||||
.. setting:: DATABASE-ENGINE
|
.. setting:: DATABASE-ENGINE
|
||||||
|
|
||||||
ENGINE
|
ENGINE
|
||||||
|
@ -1807,6 +1831,12 @@ to ensure your processes are running in the correct environment.
|
||||||
TRANSACTIONS_MANAGED
|
TRANSACTIONS_MANAGED
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
.. deprecated:: 1.6
|
||||||
|
|
||||||
|
This setting was deprecated because its name is very misleading. Use the
|
||||||
|
:setting:`AUTOCOMMIT <DATABASE-AUTOCOMMIT>` key in :setting:`DATABASES`
|
||||||
|
entries instead.
|
||||||
|
|
||||||
Default: ``False``
|
Default: ``False``
|
||||||
|
|
||||||
Set this to ``True`` if you want to :ref:`disable Django's transaction
|
Set this to ``True`` if you want to :ref:`disable Django's transaction
|
||||||
|
|
|
@ -262,9 +262,11 @@ Transaction management APIs
|
||||||
Transaction management was completely overhauled in Django 1.6, and the
|
Transaction management was completely overhauled in Django 1.6, and the
|
||||||
current APIs are deprecated:
|
current APIs are deprecated:
|
||||||
|
|
||||||
- :func:`django.db.transaction.autocommit`
|
- ``django.middleware.transaction.TransactionMiddleware``
|
||||||
- :func:`django.db.transaction.commit_on_success`
|
- ``django.db.transaction.autocommit``
|
||||||
- :func:`django.db.transaction.commit_manually`
|
- ``django.db.transaction.commit_on_success``
|
||||||
|
- ``django.db.transaction.commit_manually``
|
||||||
|
- the ``TRANSACTIONS_MANAGED`` setting
|
||||||
|
|
||||||
The reasons for this change and the upgrade path are described in the
|
The reasons for this change and the upgrade path are described in the
|
||||||
:ref:`transactions documentation <transactions-upgrading-from-1.5>`.
|
:ref:`transactions documentation <transactions-upgrading-from-1.5>`.
|
||||||
|
|
|
@ -26,45 +26,61 @@ immediately committed to the database. :ref:`See below for details
|
||||||
Previous version of Django featured :ref:`a more complicated default
|
Previous version of Django featured :ref:`a more complicated default
|
||||||
behavior <transactions-upgrading-from-1.5>`.
|
behavior <transactions-upgrading-from-1.5>`.
|
||||||
|
|
||||||
|
.. _tying-transactions-to-http-requests:
|
||||||
|
|
||||||
Tying transactions to HTTP requests
|
Tying transactions to HTTP requests
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
The recommended way to handle transactions in Web requests is to tie them to
|
A common way to handle transactions on the web is to wrap each request in a
|
||||||
the request and response phases via Django's ``TransactionMiddleware``.
|
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. When a request starts, Django starts a transaction. If the
|
It works like this. When a request starts, Django starts a transaction. If the
|
||||||
response is produced without problems, Django commits any pending transactions.
|
response is produced without problems, Django commits the transaction. If the
|
||||||
If the view function produces an exception, Django rolls back any pending
|
view function produces an exception, Django rolls back the transaction.
|
||||||
transactions.
|
Middleware always runs outside of this transaction.
|
||||||
|
|
||||||
To activate this feature, just add the ``TransactionMiddleware`` middleware to
|
You may perfom partial commits and rollbacks in your view code, typically with
|
||||||
your :setting:`MIDDLEWARE_CLASSES` setting::
|
the :func:`atomic` context manager. However, at the end of the view, either
|
||||||
|
all the changes will be committed, or none of them.
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
To disable this behavior for a specific view, you must set the
|
||||||
'django.middleware.cache.UpdateCacheMiddleware',
|
``transactions_per_request`` attribute of the view function itself to
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
``False``, like this::
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.transaction.TransactionMiddleware',
|
|
||||||
'django.middleware.cache.FetchFromCacheMiddleware',
|
|
||||||
)
|
|
||||||
|
|
||||||
The order is quite important. The transaction middleware applies not only to
|
def my_view(request):
|
||||||
view functions, but also for all middleware modules that come after it. So if
|
do_stuff()
|
||||||
you use the session middleware after the transaction middleware, session
|
my_view.transactions_per_request = False
|
||||||
creation will be part of the transaction.
|
|
||||||
|
|
||||||
The various cache middlewares are an exception: ``CacheMiddleware``,
|
.. warning::
|
||||||
:class:`~django.middleware.cache.UpdateCacheMiddleware`, and
|
|
||||||
:class:`~django.middleware.cache.FetchFromCacheMiddleware` are never affected.
|
|
||||||
Even when using database caching, Django's cache backend uses its own database
|
|
||||||
connection internally.
|
|
||||||
|
|
||||||
.. note::
|
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.
|
||||||
|
|
||||||
The ``TransactionMiddleware`` only affects the database aliased
|
.. admonition:: Per-request transactions and streaming responses
|
||||||
as "default" within your :setting:`DATABASES` setting. If you are using
|
|
||||||
multiple databases and want transaction control over databases other than
|
When a view returns a :class:`~django.http.StreamingHttpResponse`, reading
|
||||||
"default", you will need to write your own transaction middleware.
|
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 in enclosed in the transactions.
|
||||||
|
Middleware run outside of the transaction, and so does the rendering of
|
||||||
|
template responses.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
Django used to provide this feature via ``TransactionMiddleware``, which is
|
||||||
|
now deprecated.
|
||||||
|
|
||||||
Controlling transactions explicitly
|
Controlling transactions explicitly
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
@ -283,18 +299,20 @@ if autocommit is off. Django will also refuse to turn autocommit off when an
|
||||||
Deactivating transaction management
|
Deactivating transaction management
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
Control freaks can totally disable all transaction management by setting
|
You can totally disable Django's transaction management for a given database
|
||||||
:setting:`TRANSACTIONS_MANAGED` to ``True`` in the Django settings file. If
|
by setting :setting:`AUTOCOMMIT <DATABASE-AUTOCOMMIT>` to ``False`` in its
|
||||||
you do this, Django won't enable autocommit. You'll get the regular behavior
|
configuration. If you do this, Django won't enable autocommit, and won't
|
||||||
of the underlying database library.
|
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
|
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
|
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
|
where you want to run your own transaction-controlling middleware or do
|
||||||
something really strange.
|
something really strange.
|
||||||
|
|
||||||
In almost all situations, you'll be better off using the default behavior, or
|
.. versionchanged:: 1.6
|
||||||
the transaction middleware, and only modify selected functions as needed.
|
This used to be controlled by the ``TRANSACTIONS_MANAGED`` setting.
|
||||||
|
|
||||||
|
|
||||||
Database-specific notes
|
Database-specific notes
|
||||||
=======================
|
=======================
|
||||||
|
@ -459,6 +477,35 @@ atomicity of the outer block.
|
||||||
API changes
|
API changes
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
The transaction middleware applied not only to view functions, but also to
|
||||||
|
middleware modules that come 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
|
Managing transactions
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -508,6 +555,13 @@ you should now use::
|
||||||
finally:
|
finally:
|
||||||
transaction.set_autocommit(autocommit=False)
|
transaction.set_autocommit(autocommit=False)
|
||||||
|
|
||||||
|
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
|
Backwards incompatibilities
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from django.core.handlers.wsgi import WSGIHandler
|
from django.core.handlers.wsgi import WSGIHandler
|
||||||
from django.core.signals import request_started, request_finished
|
from django.core.signals import request_started, request_finished
|
||||||
from django.db import close_old_connections
|
from django.db import close_old_connections, connection
|
||||||
from django.test import RequestFactory, TestCase
|
from django.test import RequestFactory, TestCase, TransactionTestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils import six
|
|
||||||
|
|
||||||
|
|
||||||
class HandlerTests(TestCase):
|
class HandlerTests(TestCase):
|
||||||
|
@ -37,6 +36,31 @@ class HandlerTests(TestCase):
|
||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionsPerRequestTests(TransactionTestCase):
|
||||||
|
urls = 'handlers.urls'
|
||||||
|
|
||||||
|
def test_no_transaction(self):
|
||||||
|
response = self.client.get('/in_transaction/')
|
||||||
|
self.assertContains(response, 'False')
|
||||||
|
|
||||||
|
def test_auto_transaction(self):
|
||||||
|
old_atomic_requests = connection.settings_dict['ATOMIC_REQUESTS']
|
||||||
|
try:
|
||||||
|
connection.settings_dict['ATOMIC_REQUESTS'] = True
|
||||||
|
response = self.client.get('/in_transaction/')
|
||||||
|
finally:
|
||||||
|
connection.settings_dict['ATOMIC_REQUESTS'] = old_atomic_requests
|
||||||
|
self.assertContains(response, 'True')
|
||||||
|
|
||||||
|
def test_no_auto_transaction(self):
|
||||||
|
old_atomic_requests = connection.settings_dict['ATOMIC_REQUESTS']
|
||||||
|
try:
|
||||||
|
connection.settings_dict['ATOMIC_REQUESTS'] = True
|
||||||
|
response = self.client.get('/not_in_transaction/')
|
||||||
|
finally:
|
||||||
|
connection.settings_dict['ATOMIC_REQUESTS'] = old_atomic_requests
|
||||||
|
self.assertContains(response, 'False')
|
||||||
|
|
||||||
class SignalsTests(TestCase):
|
class SignalsTests(TestCase):
|
||||||
urls = 'handlers.urls'
|
urls = 'handlers.urls'
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
from django.http import HttpResponse, StreamingHttpResponse
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^regular/$', lambda request: HttpResponse(b"regular content")),
|
url(r'^regular/$', views.regular),
|
||||||
url(r'^streaming/$', lambda request: StreamingHttpResponse([b"streaming", b" ", b"content"])),
|
url(r'^streaming/$', views.streaming),
|
||||||
|
url(r'^in_transaction/$', views.in_transaction),
|
||||||
|
url(r'^not_in_transaction/$', views.not_in_transaction),
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
|
from django.http import HttpResponse, StreamingHttpResponse
|
||||||
|
|
||||||
|
def regular(request):
|
||||||
|
return HttpResponse(b"regular content")
|
||||||
|
|
||||||
|
def streaming(request):
|
||||||
|
return StreamingHttpResponse([b"streaming", b" ", b"content"])
|
||||||
|
|
||||||
|
def in_transaction(request):
|
||||||
|
return HttpResponse(str(connection.in_atomic_block))
|
||||||
|
|
||||||
|
def not_in_transaction(request):
|
||||||
|
return HttpResponse(str(connection.in_atomic_block))
|
||||||
|
not_in_transaction.transactions_per_request = False
|
Loading…
Reference in New Issue