Настройка своих шаблонов для страниц ошибок 403, 404

⏱ 20 мин · 🧩 заданий: 5

Страницы ошибок

Когда пользователь открывает несуществующую статью или пытается отредактировать чужую, он видит стандартную белую страницу «Not Found» или «Forbidden». Сделаем свои — в дизайне сайта, с меню и ссылкой на главную.

Код Когда возникает
400 Некорректный запрос (SuspiciousOperation)
403 Нет прав (PermissionDenied, handle_no_permission(), ошибка CSRF)
404 Не найдено (Http404, get_object_or_404, нет маршрута)
500 Необработанное исключение в коде

Способ 1: просто шаблоны

Положите в корень папки шаблонов файлы 404.html, 403.html, 500.html — Django найдёт их сам:

{% extends "main.html" %}
{% block content %}
  <h1>Страница не найдена</h1>
  <p>Возможно, статья была удалена или адрес набран с ошибкой.</p>
  <a href="{% url 'home' %}" class="btn btn-primary">На главную</a>
{% endblock %}

Осторожно с 500.html

Шаблон 500 рендерится без контекстных процессоров: в нём нет request и user. Если main.html обращается к ним или к базе (меню категорий!), страница ошибки сама упадёт. Делайте 500.html простым, без extends.

Способ 2: свои обработчики

Если нужна логика — свой заголовок, логирование, разные шаблоны — пишем представления-обработчики.

modules/system/views.py:

from django.shortcuts import render


def tr_handler404(request, exception):
    return render(
        request=request,
        template_name="system/errors/error_page.html",
        status=404,
        context={
            "title": "Страница не найдена: 404",
            "error_message": "К сожалению, такая страница не найдена или была перемещена",
        },
    )


def tr_handler500(request):
    return render(
        request=request,
        template_name="system/errors/error_page.html",
        status=500,
        context={
            "title": "Ошибка сервера: 500",
            "error_message": "Внутренняя ошибка сайта, вернитесь на главную страницу, отчёт об ошибке мы направим администрации сайта",
        },
    )


def tr_handler403(request, exception):
    return render(
        request=request,
        template_name="system/errors/error_page.html",
        status=403,
        context={
            "title": "Ошибка доступа: 403",
            "error_message": "Доступ к этой странице ограничен",
        },
    )

Обязательно передавайте status — иначе страница ошибки уйдёт с кодом 200, и поисковики проиндексируют её как обычную.

Регистрация — только в корневом backend/urls.py, на верхнем уровне модуля:

handler403 = "modules.system.views.tr_handler403"
handler404 = "modules.system.views.tr_handler404"
handler500 = "modules.system.views.tr_handler500"

templates/system/errors/error_page.html:

{% extends "main.html" %}
{% block content %}
  <div class="card border-0 text-center py-5">
    <h1 class="display-6">{{ title }}</h1>
    <p class="lead">{{ error_message }}</p>
    <a href="{% url 'home' %}" class="btn btn-dark">Вернуться на главную</a>
  </div>
{% endblock %}

Проверка

При DEBUG = True Django показывает отладочную страницу, а не ваши шаблоны. Чтобы увидеть свои:

DEBUG = False
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]

При DEBUG = False runserver перестанет раздавать статику — для проверки запустите python manage.py runserver --insecure.

403 из своего кода

Миксины доступа вызывают 403 через handle_no_permission(). Из функции — исключением:

from django.core.exceptions import PermissionDenied

if request.user != article.author:
    raise PermissionDenied("Редактировать может только автор")

Тесты

Тестовый клиент работает при DEBUG = False, поэтому обработчики срабатывают:

class ErrorPagesTests(TestCase):
    def test_404(self):
        response = self.client.get("/no-such-page/")
        self.assertEqual(response.status_code, 404)
        self.assertTemplateUsed(response, "system/errors/error_page.html")

Итог

  • Простой путь — шаблоны 404.html, 403.html, 500.html в корне шаблонов.
  • Гибкий путь — функции handler404/403(request, exception) и handler500(request) в корневом urls.py.
  • Всегда возвращайте правильный status.
  • Свои страницы видны только при DEBUG = False; 500.html — без зависимостей от базы и контекста.

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Напишите обработчик tr_handler404(request, exception), который рендерит шаблон system/errors/error_page.html с контекстом {"title": "Страница не найдена: 404"} и статусом 404.

Задание 2 Написать код

Зарегистрируйте обработчики в корневом backend/urls.py: handler403 = "modules.system.views.tr_handler403" и handler404 = "modules.system.views.tr_handler404".

Задание 3 Один вариант ответа

Вы создали templates/404.html, но при открытии несуществующей страницы видите жёлтую отладочную страницу Django. Почему?

Задание 4 Один вариант ответа

Чем сигнатура handler500 отличается от handler404 и handler403?

Задание 5 Написать код

Напишите тест: запрос к /no-such-page/ возвращает код 404 и использует шаблон system/errors/error_page.html (self.assertEqual(response.status_code, 404) и self.assertTemplateUsed(...)).

Войдите, чтобы отмечать пройденные уроки.