Свои шаблоны для обработки ошибок 403, 404 в django
10 февраля 2016 г. 5:53
В зависимости от задач можно по-разному переопределить в Django стандартное поведение на реакцию ошибок, таких как 403, 404, 500 и стандартные шаблоны вывода ошибок. В официальной документации (на рус. яз. - http://djbook.ru/rel1.8/topics/http/views.html) хорошо это описывается, но тем не менее есть некоторые нюансы, о которых я хочу рассказать и показать в примерах.
В большинстве случаев можно оставить стандартное поведение Django, т. е. стандартные views (из django.views.defaults), обрабатывающие ошибки и переопределить только шаблоны.
Допустим, у нас есть шаблон ошибки 404:
# 404.html {% extends "base.html" %} {% block subtitle %}Страница не найдена{% endblock %} {% block meta %}<meta name="robots" content="noindex, nofollow">{% endblock %} {% block content %} <h2>Запрашиваемая страница не найдена</h2> <p>Возможно, неправильно указан путь в адресной строке или страница была удалена.</p> <p>Возврат на <a href="/">главную страницу</a></p> {% endblock %}
А ниже описаны разные способы переопределения.
Добавление своего шаблона в templates
Самый простой способ переопределения - это добавить этот шаблон в "my_project / templates". Именно здесь django будет искать шаблон 404.html и при наличии отрендерит.
Добавление своего шаблона в другую папку
Не всегда удобно держать шаблоны ошибок в "my_project / templates" вместе с какими-то другими шаблонами и хочется их вынести в отдельную папку. Я, к примеру, использую следующую структуру папки templates в корне проекта (жирным выделены папки):
- admin - содержит переопределяемые админские шаблонов
- cms - содержит переопределяемые шаблоны Django CMS
- errs - содержит шаблоны ошибок
- 403.html
- 404.html
- 500.html
- vers - содержит проверочные файлы для поисковых систем
- base.html
- one_col.html
- two_col.html
- main.html
Как видите, вынос шаблонов ошибок в отдельную папку - хорошая идея отделить их от шаблонов страниц (one_col.html, two_col.html, main.html). Чтобы указать django, где искать шаблоны по новому пути, нужно использовать метод curry
, например:
# urls.py from django.views.defaults import server_error, page_not_found, permission_denied handler403 = curry(permission_denied, template_name='errs/403.html') handler404 = curry(page_not_found, template_name='errs/404.html') handler500 = curry(server_error, template_name='errs/500.html')
Замечание: в Django>=1.9 нужно добавить ещё один аргумент exception
для handler403
и handler404
:
handler403 = curry(permission_denied, exception=Exception('Permission Denied'), template_name='errs/403.html') handler404 = curry(page_not_found, exception=Exception('Page not Found'), template_name='errs/404.html')
Тестирование своих шаблонов ошибок
Чтобы протестировать шаблоны ошибок, достаточно добавить соответствующие urls
в urls.py, например так:
if settings.DEBUG: urlpatterns = [ url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}), url(r'', include('django.contrib.staticfiles.urls')), # --- error pages --- url(r'^403$', handler403), url(r'^404$', handler404), url(r'^500$', handler500), ] + urlpatterns
Теперь можно переходить по адресу http://localhost:8000/404 и видеть свой кастомный шаблон ошибки. На боевом сайте нам тестовые урлы ошибок не нужны, поэтому мы добавили их в условие if settings.DEBUG:
.
Создание своего представления для обработки ошибки
Если хочется изменить стандартное поведение обработки ошибок, то нужно писать своё представление. Например:
# urls.py handler403 = 'my_app.views.show_403' handler404 = 'my_app.views.show_404' # my_app/views.py # Обработка ошибки 403 from django.core.exceptions import PermissionDenied def show_403(request): # какие-либо действия raise PermissionDenied # Обработка ошибки 404 from django.http.response import Http404 def show_404(request): # какие-либо действия raise Http404
Можно усложнить пример с обработкой ошибки 404. Например, мы хотим отдавать разные шаблоны 404 в зависимости от того, с чего начинается отсутствующий url
:
# Обработка ошибки 404 from django.http.response import Http404 def 404(request): if request.path.startswith('/project/'): return render(request, 'project_not_found.html') # выдаст страницу, что проекта нет и, к примеру, покажет другие проекты if request.path.startswith('/shop/'): return render(request, 'product_not_found.html') # выдаст страницу, что товара нет и, к примеру, покажет другие товары raise Http404 # в остальных случаях показать стандартное исключение, которое отрендерит наш шаблон 404.html
Некоторые тонкости
Проверка отдачи шаблона 404
Для проверки отдачи шаблона 404 используйте при значении переменной DEBUG=False
в settings.py, иначе вам будет показан трейсбек Django.
Внимание! Если DEBUG=False
, то вы должны добавить в ALLOWED_HOSTS
(в settings.py) допустимые доменные имена, иначе Django будет выдавать ошибку “Bad Request (400)”. На локальной машине добавляется ‘localhost’ (или 127.0.0.1).
# settings.py ALLOWED_HOSTS = ['localhost', ]
Обратите внимание, что при DEBUG=False
статические файлы не будут показываться. Для показа статических файлов нужно собрать статику и запустить сервер с опцией --insecure
:
python manage.py collectstatic python manage.py runserver --insecure
Создание своего представления для обработки 500 ошибки
При использовании стандартного представления обработки 500 ошибки в соответствующий шаблон не передаётся контекст. Сделано так для того, чтобы уменьшить количество возможных других ошибок. Поэтому шаблон должен быть простой подобный этому:
<!DOCTYPE html> <html> <head> <title>Ошибка на стороне сервера</title> <meta name="robots" content="noindex, nofollow"> </head> <body> <p>Извините, но что-то случилось с сайтом.</p> <p>В техническую поддержку уже отправлено уведомление.</p> </body> </html>
Если будете делать своё представление для 500 ошибки, то следуйте правилам django - не передавайте RequestContext
при рендеринге шаблона. Посмотрите как происходит обработка в стандартном представлении django.views.defaults.server_error.
Отображение ошибок без рендеринга шаблонов
from django.http import HttpResponseNotFound def test_view_1(request, param): if not param: # какое-то условие return HttpResponseNotFound('<p>Страница не найдена</p>') return render_to_response('test_view_1.html') from django.http.response import HttpResponseForbidden def test_view_2(request, param): if not param: return HttpResponseForbidden('Доступ запрещён') return render_to_response('test_view_2.html')
Надеюсь, статья помогла ответить на возникающие вопросы об обработке ошибок в Django.
Похожие статьи:
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.
Комментарии: 0