custom-django-error-pages-preview.jpg

Custom Django Error Pages

6 months ago 706 views
3 min read

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:


Similar Posts

7 months ago
django performance django-admin ui

Best Practises for A Performant Django Admin

10 min read

The admin interface that comes with Django is one of the great things about Django. It comes with a ton of features out of the box and has many open source packages that extend the base functionality even more. Well …


4 years ago
mailgun statuscake terraform cloudflare devops s3 rds django

Kickstarting Infrastructure for Django Applications with Terraform

8 min read

When creating Django applications or using cookiecutters as Django Cookiecutter you will have by default a number of dependencies that will be needed to be created as a S3 bucket, a Postgres Database and a Mailgun domain.


4 years ago
django cms wagtail headless api

Recipes when building a headless CMS with Wagtail's API

3 min read

Recently I built a headless CMS using Wagtail's API as a backend with NextJS/React/Redux as a frontend. Building the API I ran into some small issues with Image URL data, the API representation of snippets and creating a fully customized …