Reputation: 29597
I am moving to Django 1.10 from Django 1.5 and discovering that database transactions are quite different with little commit and rollback control.
What I want:
Perform series of queries in view: my_function(request)
If all queries succeed:
commit
if any query fails:
rollback
insert into log table and commit the insertion
In Django 1.5, I handle this with rollback and commit in the Exception Middleware:
class ExceptionMiddleware(object):
def process_exception(self, request, exception):
# do rollback
# insert into log table
# commit
How can I achieve this in Django 1.10 where there doesn't seem to be a way to do rollback?
My 1.10 settings:
AUTOCOMMIT = True
ATOMIC_REQUESTS = True
This puts all queries into a single transaction that, correctly, only commits upon completion.
I would have more rollback/commit control with "AUTOCOMMIT = False," but Django recommends against this: "this is best used in situations where you want to run your own transaction-controlling middleware or do something really strange."
Upvotes: 0
Views: 456
Reputation: 29597
The following worked. When there is an exception in my_view(), its insertion is rolled back, but the insertion in the exception middleware is committed.
settings.py
Database ATOMIC_REQUESTS = True
MIDDLEWARE = [
...
'myproject.middleware.ExceptionMiddleware', # put it last
]
views.py
def my_view(request):
do table insertion insertion..
x = 1/0 # cause exception
middleware.py
from django.utils.deprecation import MiddlewareMixin
class ExceptionMiddleware(MiddlewareMixin):
def process_exception(self, request, exception):
# can be put inside "with transaction.atomic():" for more control of rollback/commit within this function
log_error() # do table insertion
# can return some response
Upvotes: 1
Reputation: 53699
Django 1.10 also introduced a new style of middleware, which makes this quite easy:
from django.db import transaction
class LoggingMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
with transaction.atomic():
response = self.get_response(request)
except:
log_failure()
raise
return response
When an exception is propagated through the __exit__
method of transaction.atomic()
, the transaction is automatically rolled back. You can then catch the exception and log the failure outside of the main transaction that encapsulates the view.
Upvotes: 2