From 2f615b10e6330d27dccbd770a4628200044acf70 Mon Sep 17 00:00:00 2001 From: ana-balica Date: Mon, 15 Jun 2015 15:55:55 +0300 Subject: [PATCH] Fixed #24829 -- Allowed use of TemplateResponse in view error handlers. --- django/core/handlers/base.py | 9 ++++++ docs/releases/1.9.txt | 4 +++ tests/handlers/templates/test_handler.html | 1 + tests/handlers/tests_custom_error_handlers.py | 30 +++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 tests/handlers/templates/test_handler.html create mode 100644 tests/handlers/tests_custom_error_handlers.py diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index cad2ba2a60..04c8f79180 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -113,6 +113,9 @@ class BaseHandler(object): urlconf = settings.ROOT_URLCONF urlresolvers.set_urlconf(urlconf) resolver = urlresolvers.RegexURLResolver(r'^/', urlconf) + # Use a flag to check if the response was rendered to prevent + # multiple renderings or to force rendering if necessary. + response_is_rendered = False try: response = None # Apply request middleware @@ -174,6 +177,7 @@ class BaseHandler(object): "HttpResponse object. It returned None instead." % (middleware_method.__self__.__class__.__name__)) response = response.render() + response_is_rendered = True except http.Http404 as exc: logger.warning('Not Found: %s', request.path, @@ -246,6 +250,11 @@ class BaseHandler(object): response._closable_objects.append(request) + # If the exception handler returns a TemplateResponse that has not + # been rendered, force it to be rendered. + if not response_is_rendered and callable(getattr(response, 'render', None)): + response = response.render() + return response def handle_uncaught_exception(self, request, resolver, exc_info): diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 3aa6c495de..63e1335333 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -385,6 +385,10 @@ Requests and Responses * The default 40x error views now accept a second positional parameter, the exception that triggered the view. +* View error handlers now support + :class:`~django.template.response.TemplateResponse`, commonly used with + class-based views. + Tests ^^^^^ diff --git a/tests/handlers/templates/test_handler.html b/tests/handlers/templates/test_handler.html new file mode 100644 index 0000000000..6f46e3e88f --- /dev/null +++ b/tests/handlers/templates/test_handler.html @@ -0,0 +1 @@ +Error handler content diff --git a/tests/handlers/tests_custom_error_handlers.py b/tests/handlers/tests_custom_error_handlers.py new file mode 100644 index 0000000000..24c2c8b446 --- /dev/null +++ b/tests/handlers/tests_custom_error_handlers.py @@ -0,0 +1,30 @@ +from django.conf.urls import url +from django.core.exceptions import PermissionDenied +from django.template.response import TemplateResponse +from django.test import SimpleTestCase, override_settings + + +def template_response_error_handler(request, exception=None): + return TemplateResponse(request, 'test_handler.html', status=403) + + +def permission_denied_view(request): + raise PermissionDenied + + +urlpatterns = [ + url(r'^$', permission_denied_view), +] + +handler403 = template_response_error_handler + + +@override_settings(ROOT_URLCONF='handlers.tests_custom_error_handlers') +class CustomErrorHandlerTests(SimpleTestCase): + + def test_handler_renders_template_response(self): + """ + BaseHandler should render TemplateResponse if necessary. + """ + response = self.client.get('/') + self.assertContains(response, 'Error handler content', status_code=403)