custom-django-error-pages-preview.jpg

Custom Django Error Pages

Published on October 05, 2023, 12:16 UTC 3 minutes 2283 views

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:

Related Posts

Best Practises for A Performant Django Admin

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 documented and works very well, the only pain point I’ve found when using the admin and its features is when I’ve had large tables containing millions of objects. In that case, searching, sorting, ordering, counting and other features cause the admin to load slowly and creates database pressure. At that point we need to optimize the admin and there are many small changes that you can do that will speed up your admin load times and reduce any additional database load. This blog post will describe approaches to the common performance problems I’ve experienced when having a large database.

Effortless Markdown Styling with Tailwind, Pygments, and Key Plugins

I’ve been running this blog for about five years, and during that time, it’s undergone several transformations. Initially, it was built with plain Markdown, HTML, and CSS. Over time, I experimented with various frameworks before finally settling on Tailwind CSS. The blog has been using Tailwind for a while now, and I’m happy with the results. In addition to Tailwind, I’ve incorporated other tools that enhance the blog’s functionality and aesthetics, such as Pygments for syntax highlighting and Medium Zoom for an interactive image zooming experience. Now that I’ve settled on a stack that I’m happy with, I thought it would be a good idea to document it. This post will cover the tools I use for my blog, including Tailwind, Pygments, Medium Zoom, and other tips & tricks.

Kickstarting Infrastructure for Django Applications with Terraform

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.

Shynet