diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 0201bd830..6bc6434b7 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -102,6 +102,18 @@ def get_concrete_model(model_cls): return model_cls +def set_rollback(transaction): + if hasattr(transaction, 'set_rollback'): + # If running in >=1.6 then mark a rollback as required, + # and allow it to be handled by Django. + transaction.set_rollback(True) + elif transaction.is_managed(): + # Otherwise handle it explicitly if in managed mode. + if transaction.is_dirty(): + transaction.rollback() + transaction.leave_transaction_management() + + # View._allowed_methods only present from 1.5 onwards if django.VERSION >= (1, 5): from django.views.generic import View diff --git a/rest_framework/views.py b/rest_framework/views.py index 292431c8a..9e971ed39 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -4,10 +4,11 @@ Provides an APIView class that is the base of all views in REST framework. from __future__ import unicode_literals from django.core.exceptions import PermissionDenied +from django.db import transaction from django.http import Http404 from django.views.decorators.csrf import csrf_exempt from rest_framework import status, exceptions -from rest_framework.compat import smart_text, HttpResponseBase, View +from rest_framework.compat import set_rollback, smart_text, HttpResponseBase, View from rest_framework.request import Request from rest_framework.response import Response from rest_framework.settings import api_settings @@ -373,6 +374,8 @@ class APIView(View): if response is None: raise + # We've swallowed the exception, but we should mark it for rollback. + set_rollback(transaction) response.exception = True return response