diff --git a/django/views/defaults.py b/django/views/defaults.py index 2bbc23321e..ec7a233ff7 100644 --- a/django/views/defaults.py +++ b/django/views/defaults.py @@ -1,6 +1,6 @@ from django import http from django.template import (Context, RequestContext, - loader, TemplateDoesNotExist) + loader, Template, TemplateDoesNotExist) from django.views.decorators.csrf import requires_csrf_token @@ -17,8 +17,13 @@ def page_not_found(request, template_name='404.html'): request_path 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. - return http.HttpResponseNotFound(t.render(RequestContext(request, {'request_path': request.path}))) + try: + template = loader.get_template(template_name) + except TemplateDoesNotExist: + template = Template( + '

Not Found

' + '

The requested URL {{ request_path }} was not found on this server.

') + return http.HttpResponseNotFound(template.render(RequestContext(request, {'request_path': request.path}))) @requires_csrf_token @@ -29,8 +34,11 @@ def server_error(request, template_name='500.html'): Templates: :template:`500.html` Context: None """ - t = loader.get_template(template_name) # You need to create a 500.html template. - return http.HttpResponseServerError(t.render(Context({}))) + try: + template = loader.get_template(template_name) + except TemplateDoesNotExist: + return http.HttpResponseServerError('

Server Error (500)

') + return http.HttpResponseServerError(template.render(Context({}))) # This can be called when CsrfViewMiddleware.process_view has not run, diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index d6f95008de..f3501026f8 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -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 ``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 -``404.html`` template in the root of your template directory. The default 404 -view will use that template for all 404 errors. If :setting:`DEBUG` is set to -``False`` (in your settings module) and if you didn't create a ``404.html`` -file, an ``Http500`` is raised instead. So remember to create a ``404.html``. +is used by default. Optionally, you can create a ``404.html`` template +in the root of your template directory. The default 404 view will then use that +template for all 404 errors when :setting:`DEBUG` is set to ``False`` (in your +settings module). A couple more things to note about 404 views: diff --git a/docs/ref/contrib/flatpages.txt b/docs/ref/contrib/flatpages.txt index 38cedc40fe..7ff9165642 100644 --- a/docs/ref/contrib/flatpages.txt +++ b/docs/ref/contrib/flatpages.txt @@ -158,9 +158,7 @@ For more on middleware, read the :doc:`middleware docs :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` 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 - raising an exception instead (such as a ``TemplateDoesNotExist`` - exception if your site does not have an appropriate template to - use for HTTP 404 responses), the response will become an HTTP 500 + raising an exception instead, the response will become an HTTP 500 ("Internal Server Error") and the :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` will not attempt to serve a flat page. diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 41fb2882a7..d87efda0af 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -185,6 +185,12 @@ Django 1.5 also includes several smaller improvements worth noting: look up permissions by using ``{% if 'someapp.someperm' in perms %}`` 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 ===================================== diff --git a/docs/topics/http/views.txt b/docs/topics/http/views.txt index c4bd15e72e..7c4d1bbb6e 100644 --- a/docs/topics/http/views.txt +++ b/docs/topics/http/views.txt @@ -134,13 +134,12 @@ The 404 (page not found) view When you raise an ``Http404`` exception, Django loads a special view devoted to handling 404 errors. By default, it's the view -``django.views.defaults.page_not_found``, which loads and renders the template -``404.html``. +``django.views.defaults.page_not_found``, which either produces a very simple +"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 -directory. This template will be used for all 404 errors. The default 404 view -will pass one variable to the template: ``request_path``, which is the URL -that resulted in the error. +The default 404 view 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 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 ``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 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 will have access to variables supplied by your :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 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 -template ``500.html``. +the view ``django.views.defaults.server_error``, which either produces a very +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 -directory. This template will be used for all server errors. The default 500 -view passes no variables to this template and is rendered with an empty -``Context`` to lessen the chance of additional errors. +The default 500 view passes no variables to the ``500.html`` 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 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 ``django.views.defaults.server_error`` if you did not define one. -Two things 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. +One thing to note about 500 views: * 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 diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 41f40e7467..a150d1ce2a 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -229,11 +229,11 @@ class Templates(unittest.TestCase): loader.template_source_loaders = (filesystem.Loader(),) # 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 # load_template method from the BaseLoader class, so we only need # to test one of them. - load_name = '404.html' + load_name = 'login.html' template = loader.get_template(load_name) template_name = template.nodelist[0].source[0].name self.assertTrue(template_name.endswith(load_name), diff --git a/tests/regressiontests/test_client_regress/tests.py b/tests/regressiontests/test_client_regress/tests.py index c741903c34..f424321663 100644 --- a/tests/regressiontests/test_client_regress/tests.py +++ b/tests/regressiontests/test_client_regress/tests.py @@ -628,15 +628,6 @@ class TemplateExceptionTests(TestCase): if hasattr(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( TEMPLATE_DIRS=(os.path.join(os.path.dirname(__file__), 'bad_templates'),) ) diff --git a/tests/regressiontests/views/tests/defaults.py b/tests/regressiontests/views/tests/defaults.py index 2dd40b4a1a..3ca7f79136 100644 --- a/tests/regressiontests/views/tests/defaults.py +++ b/tests/regressiontests/views/tests/defaults.py @@ -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.test import TestCase +from django.test.utils import setup_test_template_loader, restore_template_loaders from ..models import Author, Article, UrlArticle @@ -71,6 +72,23 @@ class DefaultsTests(TestCase): response = self.client.get('/views/server_error/') 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): "A model can set attributes on the get_absolute_url method" self.assertTrue(getattr(UrlArticle.get_absolute_url, 'purge', False), diff --git a/tests/templates/404.html b/tests/templates/404.html deleted file mode 100644 index da627e2222..0000000000 --- a/tests/templates/404.html +++ /dev/null @@ -1 +0,0 @@ -Django Internal Tests: 404 Error \ No newline at end of file diff --git a/tests/templates/500.html b/tests/templates/500.html deleted file mode 100644 index ff028cbeb0..0000000000 --- a/tests/templates/500.html +++ /dev/null @@ -1 +0,0 @@ -Django Internal Tests: 500 Error \ No newline at end of file