Django comes with default views for 400, 403, 404 and 500 pages which is great. However, the default templates for these views are minimal - they just indicate what error it is. This blog post will walk through how to add custom templates and views for the error pages.
The Django error templates used in this post are packaged and published to PyPI. The django-custom-error-views repository has details on how to use the package.
Using Custom Error Templates
Django tries by default to use the templates 400.html
, 403.html
, 404.html
and 500.html
in the root <project>/templates
directory. Therefore, you can just add them and Django will use those by default. Here is a sample project showing this. As you can see, there are 4 templates for all the 400–500 errors, one of these templates is for example the 400.html
that adds a logo, a return to home button, an error image and a description. There's a preview here.
400.html
{% extends "django_custom_error_views/base.html" %}
{% load static %}
{% block title %}
Bad Request
{% endblock title %}
{% block content %}
{% if company_logo %}
<a href="/">
<img class="mt-5"
style="max-height: 45px"
src="{{ STATIC_URL }}/{{ company_logo }}"
onerror="this.onerror=null; this.src='{{ company_logo }}'"
alt="Company Logo">
</a>
{% endif %}
<img class="mt-5"
style="max-height: 500px"
src="{% static image|default:'django_custom_error_views/visual-400.jpg' %}"
alt="Error image">
<p class="font-bold text-3xl font-[Poppins] text-center">{{ title|default:"Error, bad request." }}</p>
<p class="mt-5 max-w-xl text-center">
{{ description|default:"This ia a malformed or illegal request. You can find loads to explore on the home page." }}
</p>
<p class="mt-2 max-w-xl text-center">{{ extra_content }}</p>
{% if exception and render_exception %}
<p class="mt-2 max-w-xl text-center">The following exception was raised: {{ exception }}</p>
{% endif %}
<a type="button"
class="mt-5 rounded-full font-semibold bg-white px-4 py-2.5 text-sm text-blue-800 shadow-sm ring-1 ring-inset ring-blue-800 hover:bg-gray-50"
href="/">Return Home</a>
{% endblock %}
The above template is a bit complex, and you can make it much easier. The above template also depends on context being added when rendering the template. This can be done by using custom views.
Using Custom Views
To add context to the templates, we'll need a custom view. Let's take a look at a 500 error view.
@requires_csrf_token
def handler500(request, template_name="500.html"):
"""
500 error handler.
"""
error_page_title = getattr(settings, "ERROR_PAGE_TITLE", None)
error_page_description = getattr(settings, "ERROR_PAGE_description", None)
context = {
"error_page_title": error_page_title
"error_page_description": error_page_description
}
template = loader.get_template(template_name)
body = template.render(context, request)
return HttpResponseServerError(body)
Now you can use the context variables in your template.
...
<p>{{ error_page_title }}</p>
<p>{{ error_page_description }}</p>
...
However, for Django to use your custom error handler, you need to add the following snippet to your urls.py
file.
handler400 = "<my_project>.views.handler400"
handler403 = "<my_project>.views.handler403"
handler404 = "<my_project>.views.handler404"
handler500 = "<my_project>.views.handler500"
Now you'll have custom views and templates for your error pages, and you can adjust and change everything you want.
Open Source Package for Custom Error Views
As mentioned previously, I've packaged the above into a Django package that's published on PyPI, and it's fully customizable. The installation is straightforward.
pip install django-custom-error-views
Add the package to installed apps.
INSTALLED_APPS = [
"django_custom_error_views",
]
Now Django will use the error templates in the package! That is really all! However, if you want to customize them, you need to replace the error handlers by adding the package's error handlers to urls.py
.
handler400 = "django_custom_error_views.views.handler400"
handler403 = "django_custom_error_views.views.handler403"
handler404 = "django_custom_error_views.views.handler404"
handler500 = "django_custom_error_views.views.handler500"
Now you can override all the pages and add your own branding through Django settings.
DJANGO_CUSTOM_ERROR_VIEWS = {
"company_logo": "/images/icon.png", # Static image or full URL
"400": {
"title": "Custom 400 error.",
"description": "Custom 400 description.",
"extra_content": "400 extras.",
},
"403": {
"title": "Custom 403 Error.",
"description": "Custom 403 description.",
"extra_content": "403 extras.",
"render_exception": True,
},
...
Check out the package on GitHub for all the settings you can adjust.
You can preview the error pages live at:
- 400 HTTP status code
- 403 HTTP status code
- 404 HTTP status code
- 500 HTTP status code - Has the status code 200 only in the demo.