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:
parent
864a0514b8
commit
8bd7b598b6
|
@ -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,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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'),)
|
||||||
)
|
)
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Django Internal Tests: 404 Error
|
|
|
@ -1 +0,0 @@
|
||||||
Django Internal Tests: 500 Error
|
|
Loading…
Reference in New Issue