Fixed #18807 -- Made 404.html and 500.html optional

Thanks Aymeric Augustin for the report and Jannis Leidel for the
review.
This commit is contained in:
Claude Paroz 2012-09-30 23:16:14 +02:00
parent 864a0514b8
commit 8bd7b598b6
10 changed files with 58 additions and 50 deletions

View File

@ -1,6 +1,6 @@
from django import http from django import http
from django.template import (Context, RequestContext, from django.template import (Context, RequestContext,
loader, TemplateDoesNotExist) loader, Template, TemplateDoesNotExist)
from django.views.decorators.csrf import requires_csrf_token from django.views.decorators.csrf import requires_csrf_token
@ -17,8 +17,13 @@ def page_not_found(request, template_name='404.html'):
request_path request_path
The path of the requested URL (e.g., '/app/pages/bad_page/') The path of the requested URL (e.g., '/app/pages/bad_page/')
""" """
t = loader.get_template(template_name) # You need to create a 404.html template. try:
return http.HttpResponseNotFound(t.render(RequestContext(request, {'request_path': request.path}))) template = loader.get_template(template_name)
except TemplateDoesNotExist:
template = Template(
'<h1>Not Found</h1>'
'<p>The requested URL {{ request_path }} was not found on this server.</p>')
return http.HttpResponseNotFound(template.render(RequestContext(request, {'request_path': request.path})))
@requires_csrf_token @requires_csrf_token
@ -29,8 +34,11 @@ def server_error(request, template_name='500.html'):
Templates: :template:`500.html` Templates: :template:`500.html`
Context: None Context: None
""" """
t = loader.get_template(template_name) # You need to create a 500.html template. try:
return http.HttpResponseServerError(t.render(Context({}))) template = loader.get_template(template_name)
except TemplateDoesNotExist:
return http.HttpResponseServerError('<h1>Server Error (500)</h1>')
return http.HttpResponseServerError(template.render(Context({})))
# This can be called when CsrfViewMiddleware.process_view has not run, # This can be called when CsrfViewMiddleware.process_view has not run,

View File

@ -366,11 +366,10 @@ special: It's just a normal view.
You normally won't have to bother with writing 404 views. If you don't set You normally won't have to bother with writing 404 views. If you don't set
``handler404``, the built-in view :func:`django.views.defaults.page_not_found` ``handler404``, the built-in view :func:`django.views.defaults.page_not_found`
is used by default. In this case, you still have one obligation: create a is used by default. Optionally, you can create a ``404.html`` template
``404.html`` template in the root of your template directory. The default 404 in the root of your template directory. The default 404 view will then use that
view will use that template for all 404 errors. If :setting:`DEBUG` is set to template for all 404 errors when :setting:`DEBUG` is set to ``False`` (in your
``False`` (in your settings module) and if you didn't create a ``404.html`` settings module).
file, an ``Http500`` is raised instead. So remember to create a ``404.html``.
A couple more things to note about 404 views: A couple more things to note about 404 views:

View File

@ -158,9 +158,7 @@ For more on middleware, read the :doc:`middleware docs
:class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`
only steps in once another view has successfully produced a 404 response. only steps in once another view has successfully produced a 404 response.
If another view or middleware class attempts to produce a 404 but ends up If another view or middleware class attempts to produce a 404 but ends up
raising an exception instead (such as a ``TemplateDoesNotExist`` raising an exception instead, the response will become an HTTP 500
exception if your site does not have an appropriate template to
use for HTTP 404 responses), the response will become an HTTP 500
("Internal Server Error") and the ("Internal Server Error") and the
:class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`
will not attempt to serve a flat page. will not attempt to serve a flat page.

View File

@ -185,6 +185,12 @@ Django 1.5 also includes several smaller improvements worth noting:
look up permissions by using ``{% if 'someapp.someperm' in perms %}`` look up permissions by using ``{% if 'someapp.someperm' in perms %}``
in templates. in templates.
* It's not required any more to have ``404.html`` and ``500.html`` templates in
the root templates directory. Django will output some basic error messages for
both situations when those templates are not found. Of course, it's still
recommended as good practice to provide those templates in order to present
pretty error pages to the user.
Backwards incompatible changes in 1.5 Backwards incompatible changes in 1.5
===================================== =====================================

View File

@ -134,13 +134,12 @@ The 404 (page not found) view
When you raise an ``Http404`` exception, Django loads a special view devoted When you raise an ``Http404`` exception, Django loads a special view devoted
to handling 404 errors. By default, it's the view to handling 404 errors. By default, it's the view
``django.views.defaults.page_not_found``, which loads and renders the template ``django.views.defaults.page_not_found``, which either produces a very simple
``404.html``. "Not Found" message or loads and renders the template ``404.html`` if you
created it in your root template directory.
This means you need to define a ``404.html`` template in your root template The default 404 view will pass one variable to the template: ``request_path``,
directory. This template will be used for all 404 errors. The default 404 view which is the URL that resulted in the error.
will pass one variable to the template: ``request_path``, which is the URL
that resulted in the error.
The ``page_not_found`` view should suffice for 99% of Web applications, but if The ``page_not_found`` view should suffice for 99% of Web applications, but if
you want to override it, you can specify ``handler404`` in your URLconf, like you want to override it, you can specify ``handler404`` in your URLconf, like
@ -152,15 +151,11 @@ Behind the scenes, Django determines the 404 view by looking for
``handler404`` in your root URLconf, and falling back to ``handler404`` in your root URLconf, and falling back to
``django.views.defaults.page_not_found`` if you did not define one. ``django.views.defaults.page_not_found`` if you did not define one.
Four things to note about 404 views: Three things to note about 404 views:
* The 404 view is also called if Django doesn't find a match after * The 404 view is also called if Django doesn't find a match after
checking every regular expression in the URLconf. checking every regular expression in the URLconf.
* If you don't define your own 404 view — and simply use the default,
which is recommended — you still have one obligation: you must create a
``404.html`` template in the root of your template directory.
* The 404 view is passed a :class:`~django.template.RequestContext` and * The 404 view is passed a :class:`~django.template.RequestContext` and
will have access to variables supplied by your will have access to variables supplied by your
:setting:`TEMPLATE_CONTEXT_PROCESSORS` setting (e.g., ``MEDIA_URL``). :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting (e.g., ``MEDIA_URL``).
@ -176,13 +171,12 @@ The 500 (server error) view
Similarly, Django executes special-case behavior in the case of runtime errors Similarly, Django executes special-case behavior in the case of runtime errors
in view code. If a view results in an exception, Django will, by default, call in view code. If a view results in an exception, Django will, by default, call
the view ``django.views.defaults.server_error``, which loads and renders the the view ``django.views.defaults.server_error``, which either produces a very
template ``500.html``. simple "Server Error" message or loads and renders the template ``500.html`` if
you created it in your root template directory.
This means you need to define a ``500.html`` template in your root template The default 500 view passes no variables to the ``500.html`` template and is
directory. This template will be used for all server errors. The default 500 rendered with an empty ``Context`` to lessen the chance of additional errors.
view passes no variables to this template and is rendered with an empty
``Context`` to lessen the chance of additional errors.
This ``server_error`` view should suffice for 99% of Web applications, but if This ``server_error`` view should suffice for 99% of Web applications, but if
you want to override the view, you can specify ``handler500`` in your URLconf, you want to override the view, you can specify ``handler500`` in your URLconf,
@ -194,11 +188,7 @@ Behind the scenes, Django determines the 500 view by looking for
``handler500`` in your root URLconf, and falling back to ``handler500`` in your root URLconf, and falling back to
``django.views.defaults.server_error`` if you did not define one. ``django.views.defaults.server_error`` if you did not define one.
Two things to note about 500 views: One thing to note about 500 views:
* If you don't define your own 500 view — and simply use the default,
which is recommended — you still have one obligation: you must create a
``500.html`` template in the root of your template directory.
* If :setting:`DEBUG` is set to ``True`` (in your settings module), then * 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

View File

@ -229,11 +229,11 @@ class Templates(unittest.TestCase):
loader.template_source_loaders = (filesystem.Loader(),) loader.template_source_loaders = (filesystem.Loader(),)
# We rely on the fact that runtests.py sets up TEMPLATE_DIRS to # We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
# point to a directory containing a 404.html file. Also that # point to a directory containing a login.html file. Also that
# the file system and app directories loaders both inherit the # the file system and app directories loaders both inherit the
# load_template method from the BaseLoader class, so we only need # load_template method from the BaseLoader class, so we only need
# to test one of them. # to test one of them.
load_name = '404.html' load_name = 'login.html'
template = loader.get_template(load_name) template = loader.get_template(load_name)
template_name = template.nodelist[0].source[0].name template_name = template.nodelist[0].source[0].name
self.assertTrue(template_name.endswith(load_name), self.assertTrue(template_name.endswith(load_name),

View File

@ -628,15 +628,6 @@ class TemplateExceptionTests(TestCase):
if hasattr(template_loader, 'reset'): if hasattr(template_loader, 'reset'):
template_loader.reset() template_loader.reset()
@override_settings(TEMPLATE_DIRS=(),)
def test_no_404_template(self):
"Missing templates are correctly reported by test client"
try:
response = self.client.get("/no_such_view/")
self.fail("Should get error about missing template")
except TemplateDoesNotExist:
pass
@override_settings( @override_settings(
TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'bad_templates'),) TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'bad_templates'),)
) )

View File

@ -1,7 +1,8 @@
from __future__ import absolute_import from __future__ import absolute_import, unicode_literals
from django.test import TestCase
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.test.utils import setup_test_template_loader, restore_template_loaders
from ..models import Author, Article, UrlArticle from ..models import Author, Article, UrlArticle
@ -71,6 +72,23 @@ class DefaultsTests(TestCase):
response = self.client.get('/views/server_error/') response = self.client.get('/views/server_error/')
self.assertEqual(response.status_code, 500) self.assertEqual(response.status_code, 500)
def test_custom_templates(self):
"""
Test that 404.html and 500.html templates are picked by their respective
handler.
"""
setup_test_template_loader(
{'404.html': 'This is a test template for a 404 error.',
'500.html': 'This is a test template for a 500 error.'}
)
try:
for code, url in ((404, '/views/non_existing_url/'), (500, '/views/server_error/')):
response = self.client.get(url)
self.assertContains(response, "test template for a %d error" % code,
status_code=code)
finally:
restore_template_loaders()
def test_get_absolute_url_attributes(self): def test_get_absolute_url_attributes(self):
"A model can set attributes on the get_absolute_url method" "A model can set attributes on the get_absolute_url method"
self.assertTrue(getattr(UrlArticle.get_absolute_url, 'purge', False), self.assertTrue(getattr(UrlArticle.get_absolute_url, 'purge', False),

View File

@ -1 +0,0 @@
Django Internal Tests: 404 Error

View File

@ -1 +0,0 @@
Django Internal Tests: 500 Error