Fixed #25697 -- Made default error views error when passed a nonexistent template_name.

This commit is contained in:
Iacopo Spalletti 2015-11-07 17:24:07 +01:00 committed by Tim Graham
parent 8d5d472c6d
commit 21bf685f5e
4 changed files with 66 additions and 5 deletions

View File

@ -4,12 +4,17 @@ from django.utils import six
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.views.decorators.csrf import requires_csrf_token from django.views.decorators.csrf import requires_csrf_token
ERROR_404_TEMPLATE_NAME = '404.html'
ERROR_403_TEMPLATE_NAME = '403.html'
ERROR_400_TEMPLATE_NAME = '400.html'
ERROR_500_TEMPLATE_NAME = '500.html'
# This can be called when CsrfViewMiddleware.process_view has not run, # This can be called when CsrfViewMiddleware.process_view has not run,
# therefore need @requires_csrf_token in case the template needs # therefore need @requires_csrf_token in case the template needs
# {% csrf_token %}. # {% csrf_token %}.
@requires_csrf_token @requires_csrf_token
def page_not_found(request, exception, template_name='404.html'): def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME):
""" """
Default 404 handler. Default 404 handler.
@ -40,6 +45,9 @@ def page_not_found(request, exception, template_name='404.html'):
body = template.render(context, request) body = template.render(context, request)
content_type = None # Django will use DEFAULT_CONTENT_TYPE content_type = None # Django will use DEFAULT_CONTENT_TYPE
except TemplateDoesNotExist: except TemplateDoesNotExist:
if template_name != ERROR_404_TEMPLATE_NAME:
# Reraise if it's a missing custom template.
raise
template = Engine().from_string( template = Engine().from_string(
'<h1>Not Found</h1>' '<h1>Not Found</h1>'
'<p>The requested URL {{ request_path }} was not found on this server.</p>') '<p>The requested URL {{ request_path }} was not found on this server.</p>')
@ -49,7 +57,7 @@ def page_not_found(request, exception, template_name='404.html'):
@requires_csrf_token @requires_csrf_token
def server_error(request, template_name='500.html'): def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
""" """
500 error handler. 500 error handler.
@ -59,12 +67,15 @@ def server_error(request, template_name='500.html'):
try: try:
template = loader.get_template(template_name) template = loader.get_template(template_name)
except TemplateDoesNotExist: except TemplateDoesNotExist:
if template_name != ERROR_500_TEMPLATE_NAME:
# Reraise if it's a missing custom template.
raise
return http.HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html') return http.HttpResponseServerError('<h1>Server Error (500)</h1>', content_type='text/html')
return http.HttpResponseServerError(template.render()) return http.HttpResponseServerError(template.render())
@requires_csrf_token @requires_csrf_token
def bad_request(request, exception, template_name='400.html'): def bad_request(request, exception, template_name=ERROR_400_TEMPLATE_NAME):
""" """
400 error handler. 400 error handler.
@ -74,6 +85,9 @@ def bad_request(request, exception, template_name='400.html'):
try: try:
template = loader.get_template(template_name) template = loader.get_template(template_name)
except TemplateDoesNotExist: except TemplateDoesNotExist:
if template_name != ERROR_400_TEMPLATE_NAME:
# Reraise if it's a missing custom template.
raise
return http.HttpResponseBadRequest('<h1>Bad Request (400)</h1>', content_type='text/html') return http.HttpResponseBadRequest('<h1>Bad Request (400)</h1>', content_type='text/html')
# No exception content is passed to the template, to not disclose any sensitive information. # No exception content is passed to the template, to not disclose any sensitive information.
return http.HttpResponseBadRequest(template.render()) return http.HttpResponseBadRequest(template.render())
@ -83,7 +97,7 @@ def bad_request(request, exception, template_name='400.html'):
# therefore need @requires_csrf_token in case the template needs # therefore need @requires_csrf_token in case the template needs
# {% csrf_token %}. # {% csrf_token %}.
@requires_csrf_token @requires_csrf_token
def permission_denied(request, exception, template_name='403.html'): def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME):
""" """
Permission denied (403) handler. Permission denied (403) handler.
@ -96,6 +110,9 @@ def permission_denied(request, exception, template_name='403.html'):
try: try:
template = loader.get_template(template_name) template = loader.get_template(template_name)
except TemplateDoesNotExist: except TemplateDoesNotExist:
if template_name != ERROR_403_TEMPLATE_NAME:
# Reraise if it's a missing custom template.
raise
return http.HttpResponseForbidden('<h1>403 Forbidden</h1>', content_type='text/html') return http.HttpResponseForbidden('<h1>403 Forbidden</h1>', content_type='text/html')
return http.HttpResponseForbidden( return http.HttpResponseForbidden(
template.render(request=request, context={'exception': force_text(exception)}) template.render(request=request, context={'exception': force_text(exception)})

View File

@ -93,6 +93,10 @@ Three things to note about 404 views:
second parameter, the exception that triggered the error. A useful second parameter, the exception that triggered the error. A useful
representation of the exception is also passed in the template context. representation of the exception is also passed in the template context.
.. versionchanged:: 1.10
Passing a nonexistent ``template_name`` will raise ``TemplateDoesNotExist``.
.. _http_internal_server_error_view: .. _http_internal_server_error_view:
The 500 (server error) view The 500 (server error) view
@ -113,6 +117,10 @@ If :setting:`DEBUG` is set to ``True`` (in your settings module), then
your 500 view will never be used, and the traceback will be displayed your 500 view will never be used, and the traceback will be displayed
instead, with some debug information. instead, with some debug information.
.. versionchanged:: 1.10
Passing a nonexistent ``template_name`` will raise ``TemplateDoesNotExist``.
.. _http_forbidden_view: .. _http_forbidden_view:
The 403 (HTTP Forbidden) view The 403 (HTTP Forbidden) view
@ -148,6 +156,10 @@ view you can use code like this::
unicode representation of the exception is also passed in the template unicode representation of the exception is also passed in the template
context. context.
.. versionchanged:: 1.10
Passing a nonexistent ``template_name`` will raise ``TemplateDoesNotExist``.
.. _http_bad_request_view: .. _http_bad_request_view:
The 400 (bad request) view The 400 (bad request) view
@ -173,3 +185,7 @@ filesystem paths.
The signature of ``bad_request()`` changed in Django 1.9. The function The signature of ``bad_request()`` changed in Django 1.9. The function
now accepts a second parameter, the exception that triggered the error. now accepts a second parameter, the exception that triggered the error.
.. versionchanged:: 1.10
Passing a nonexistent ``template_name`` will raise ``TemplateDoesNotExist``.

View File

@ -464,6 +464,9 @@ Miscellaneous
now better to call the ``LogEntry.get_change_message()`` method which will now better to call the ``LogEntry.get_change_message()`` method which will
provide the message in the current language. provide the message in the current language.
* The default error views now raise ``TemplateDoesNotExist`` if a nonexistent
``template_name`` is specified.
.. _deprecated-features-1.10: .. _deprecated-features-1.10:
Features deprecated in 1.10 Features deprecated in 1.10

View File

@ -4,8 +4,13 @@ import datetime
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.test import TestCase from django.http import Http404
from django.template import TemplateDoesNotExist
from django.test import RequestFactory, TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.views.defaults import (
bad_request, page_not_found, permission_denied, server_error,
)
from ..models import Article, Author, UrlArticle from ..models import Article, Author, UrlArticle
@ -123,3 +128,23 @@ class DefaultsTests(TestCase):
response = self.client.get('/server_error/') response = self.client.get('/server_error/')
self.assertEqual(response['Content-Type'], 'text/html') self.assertEqual(response['Content-Type'], 'text/html')
def test_custom_templates_wrong(self):
"""
Default error views should raise TemplateDoesNotExist when passed a
template that doesn't exist.
"""
rf = RequestFactory()
request = rf.get('/')
with self.assertRaises(TemplateDoesNotExist):
bad_request(request, Exception(), template_name='nonexistent')
with self.assertRaises(TemplateDoesNotExist):
permission_denied(request, Exception(), template_name='nonexistent')
with self.assertRaises(TemplateDoesNotExist):
page_not_found(request, Http404(), template_name='nonexistent')
with self.assertRaises(TemplateDoesNotExist):
server_error(request, template_name='nonexistent')