Настройка своих шаблонов для страниц ошибок 403, 404
Страницы ошибок
Когда пользователь открывает несуществующую статью или пытается отредактировать чужую, он видит стандартную белую страницу «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
Напишите обработчик tr_handler404(request, exception), который рендерит шаблон
system/errors/error_page.html с контекстом {"title": "Страница не найдена: 404"} и статусом 404.
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"},
)
Зарегистрируйте обработчики в корневом backend/urls.py:
handler403 = "modules.system.views.tr_handler403" и handler404 = "modules.system.views.tr_handler404".
urlpatterns = [
path("admin/", admin.site.urls),
]
handler403 = "modules.system.views.tr_handler403"
handler404 = "modules.system.views.tr_handler404"
Вы создали templates/404.html, но при открытии несуществующей страницы видите жёлтую отладочную страницу Django. Почему?
Чем сигнатура handler500 отличается от handler404 и handler403?
Напишите тест: запрос к /no-such-page/ возвращает код 404 и использует шаблон
system/errors/error_page.html (self.assertEqual(response.status_code, 404) и self.assertTemplateUsed(...)).
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")
Войдите, чтобы отмечать пройденные уроки.