В зависимости от задач можно по-разному переопределить в 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.
Оцените статью
3.9 из 5 (всего 7 оценок)
После нажатия кнопки «Отправить» ваше сообщение будет доставлено мне на почту.
Артём Мальцев
Веб-разработчик, владеющий знаниями языка программирования Python, фреймворка Django, системы управления содержимым сайта Django CMS, платформы для создания интернет-магазина Django Shop и многих различных приложений, использующих эти технологии.
Права на использование материала, расположенного на этой странице https://vivazzi.pro/ru/it/django-custom-templates-for-errors/:
Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow"
в теге <a>
. Использование:
Автор статьи: Артём Мальцев
Ссылка на статью: <a href="https://vivazzi.pro/ru/it/django-custom-templates-for-errors/">https://vivazzi.pro/ru/it/django-custom-templates-for-errors/</a>
Больше: Правила использования сайта
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.
A quick noob question here! How to set a theme to use a custom template for 404 error pages instead of using page.tpl ?
asked Jun 16, 2012 at 10:54
rxjsisfinerxjsisfine
2331 gold badge2 silver badges5 bronze badges
0
Follow these steps:
-
Make a new page using the Panels module (Empty page or put in it any content)
-
In admin/config/system/site-information set Default 404 (not found) page to your new page
-
Override the template file for that page (page—error.tpl.php)
-
Remove all the regions you don’t need and put your code
apaderno♦
96.2k15 gold badges157 silver badges283 bronze badges
answered Jun 16, 2012 at 11:10
AhmedAhmed
1,4373 gold badges17 silver badges29 bronze badges
3
Yet another solution from this discussion that works without panels or other custom modules.
In your theme folder locate template.php
and write this code. Replace YOURTHEME
with the name of your theme:
function YOURTHEME_preprocess_page(&$vars) {
$header = drupal_get_http_header('status');
if ($header == '404 Not Found') {
$vars['theme_hook_suggestions'][] = 'page__404';
}
}
Whenever 404 occurs your page--404.tpl.php
will be used. Remember to put «—» not «__» in your template file name.
answered Feb 9, 2015 at 11:31
danielsdaniels
8641 gold badge9 silver badges19 bronze badges
3
If you want to have a customized 404 page that doesn’t involve creating a page (node), use the customerror module.
If you have a 404 page in Drupal, then it’s going to show up in search results, and/or your Views listings.
When using the customerror module, you can override the template by creating a page—customerror.tpl.php in your theme folder. Remember also to flush your cache after you’ve created new template file.
answered Jun 17, 2013 at 1:58
alexkbalexkb
5882 silver badges11 bronze badges
2
These days you get a page--404.html.tpl
and a page--4xx.html.tpl
theme suggestion out of the box.
answered Oct 18, 2020 at 23:28
DarvanenDarvanen
4,05315 silver badges39 bronze badges
Without assuming any convention which may exist in your theme base, the question is finally how can I get to know for sure what template file to use …
Here are the steps :
- Create a basic base and call it as you want.
- Assign that page to the 404 response admin/config/system/site-information
- Enable the theme_debug mode.
- Type a stupid url in your address bar.
- You arrive on your page initially created
-
Look at the source and the templates suggestion, you will get something like :
-
That’s straight forward, no preprocess file or template.php to modify.
Myself I have choseen the one marked by a x, as I wanted something naked with afull frame pic and just a link to back.
answered Jul 13, 2016 at 8:27
jmaryjmary
2561 silver badge6 bronze badges
1
Яндекс.Практикум
курс Python-разработчик
студент Leonid Slavutin
Проект sprint_6. Подписки на авторов.
Шаблоны и структура проекта заданы.
Задачи проекта:
-
В проект добавлены кастомные страницы ошибок:
404 page_not_found
500 server_error
403 permission_denied_view
Написан тест, проверяющий, что страница 404 отдает кастомный шаблон.
-
С помощью sorl-thumbnail выведены иллюстрации к постам.
Написаны тесты, которые проверяют работу с изображениями.
-
Создана система комментариев
-
Добавлены:
Кеширование главной страницы
Тестирование кэша
-
Добавлена система подписки на авторов.
Разворачивание проекта:
Клонировать репозиторий и перейти в его папку в командной строке:
git clone https://github.com/coherentus/hw05_final
cd hw05_final
Cоздать и активировать виртуальное окружение:
Для *nix-систем и MacOS:
Для windows-систем:
source venv/Scripts/activate
Установить зависимости из файла requirements.txt:
python3 -m pip install --upgrade pip
pip install -r requirements.txt
Выполнить миграции:
cd yatube
python3 manage.py migrate
Запустить проект:
python3 manage.py runserver
Создать суперпользователя Django:
python3 manage.py createsuperuser
Сам проект и админ-панель по адресам:
http://127.0.0.1:8000
http://127.0.0.1:8000/admin
I am trying to automate 404 pages testing using Django 1.4’s testing framework.
If I print 127.0.0.1:8000/something/really/weird/
in browser address bar with development server running, I see a 404 page, with correct «404 NOT FOUND» status (as firebug shows).
But if I try to use this code for testing:
from django.test import TestCase
class Sample404TestCase(TestCase):
def test_wrong_uri_returns_404(self):
response = self.client.get('something/really/weird/')
self.assertEqual(response.status_code, 404)
the test fails with this output:
$./manage.py test main
Creating test database for alias 'default'...
.F
======================================================================
FAIL: test_wrong_uri_returns_404 (main.tests.Sample404TestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File ".../main/tests.py", line 12, in test_wrong_uri_returns_404
self.assertEqual(response.status_code, 404)
*AssertionError: 200 != 404*
----------------------------------------------------------------------
Ran 2 tests in 0.031s
FAILED (failures=1)
Destroying test database for alias 'default'...
I’m seriously surprised with getting 200 code here. Anyone have any idea why on earth this is happening?
updated:
here lies urls.py: http://pastebin.com/DikAVa8T
and actual failing test is:
def test_wrong_uri_returns_404(self):
response = self.client.get('/something/really/weird/')
self.assertEqual(response.status_code, 404)
everything is happening in project https://github.com/gbezyuk/django-app-skeleton
- 1. Кастомизация ошибок 404, 500
- 2. Кастомизация ошибки 403
Многие ресурсы имеют оформленные страницы ошибок, если происходит сбой в обработке запроса от клиента.
Для начала на сайте была сделана кастомизация наиболее часто возникающих ошибок, другие при отладке пока не попадались, но всё впереди.
Как объявлено в заголовке статьи, кастомизированы был следующие ошибки:
- 403 — Ошибка авторизации, доступ запрещён.
- 404 — Страница не найдена;
- 500 — Внутренняя ошибка сервера;
Кастомизация ошибок 404, 500
Для кастомизации ошибок 404 и 500 необходимо написать обработчики запросов, и достаточно написать их представления в виде метода.
В шаблоны добавляем свои кастомизированные html файлы, то есть:
- error404.html
- error500.html
Модуль, в котором реализованы представления для данного сайта — это
home.
В шаблонах этого же модуля помещены сами шаблоны кастомизированных ошибок.
В файле
urls.py
главного модуля сайта переопределяем обработчики по умолчанию:
- handler404
- handler500
В коде это выглядит так:
from home.views import e_handler404, e_handler500 handler404 = e_handler404 handler500 = e_handler500Опишем представления в файле
views.py
модуля
home:
from django.shortcuts import render_to_response from django.template import RequestContext def e_handler404(request): context = RequestContext(request) response = render_to_response('error404.html', context) response.status_code = 404 return response def e_handler500(request): context = RequestContext(request) response = render_to_response('error500.html', context) response.status_code = 500 return responseКастомизация ошибки 403
Ошибка 403 возникает в том случае, когда не авторизованный пользователь пытается получить доступ к той части сайта, в которую доступ разрешён только авторизованным пользователям.
В Django это достигается за счёт проверки статуса пользователя и добавления на страницы защитного токена, механизм
CSRF.
Данная ошибка может возникнуть и в том случае, если пользователь авторизован, но совершает действия, при которых требуется проверка токена CSRF, а сам токен был потерян или не верен. Дело в том, что для корректности работы токена, необходимо добавлять в шаблоне в формах специальный тег:{% csrf_token %}В него и будет подставляться токен, но просто добавить в шаблон, его не достаточно. Прежде, чем начать рендер шаблон, необходимо добавить токен в контекст, который будет передан в шаблон. То есть,
from django.template.context_processors import csrf from django.shortcuts import render_to_response def any_request(request): context = {} context.update(csrf(request)) ... return render_to_response('any_request.html', context=context)Ну а теперь ближе к непосредственно кастомизации. Для работы csrf необходимо, чтобы в файле
settings.py
добавлен модуль csrf и указано представление, которое будет заниматься обработкой данной ошибки:MIDDLEWARE = [ ... 'django.middleware.csrf.CsrfViewMiddleware', ... ] CSRF_FAILURE_VIEW = 'home.views.csrf_failure'В шаблонах добавим
error403.html,
а в файле
views.py
пропишем обработчик представления.def csrf_failure(request, reason=""): context = RequestContext(request) response = render_to_response('error403.html', context) response.status_code = 403 return responseДля
Django
рекомендуюVDS-сервера хостера Timeweb
.