add: шаблон для ошибки (1) 404
This commit is contained in:
@@ -1425,3 +1425,38 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
|
||||
# 5. Проверка 404 для несуществующего слага
|
||||
resp_404 = self.client.get("/blog/non-existent-slug-xyz")
|
||||
self.assertEqual(resp_404.status_code, 404)
|
||||
|
||||
@override_settings(DEBUG=False)
|
||||
def test_custom_404_page_rendering(self):
|
||||
"""Проверка отдачи кастомной страницы 404 в режиме DEBUG=False."""
|
||||
response = self.client.get("/totally-non-existent-hypno-route-404")
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertContains(response, "Гипноузел не обнаружен", status_code=404)
|
||||
self.assertContains(response, "СБОЙ МАТРИЦЫ // КОД? 404", status_code=404)
|
||||
self.assertContains(response, "return-btn", status_code=404)
|
||||
self.assertContains(response, "Стираем остатки несуществующей страницы", status_code=404)
|
||||
|
||||
@override_settings(DEBUG=True)
|
||||
def test_debug_error_preview_routing(self):
|
||||
"""Проверка dev-маршрутизации предпросмотра страниц ошибок при DEBUG=True."""
|
||||
# 1. Индексный список страниц ошибок
|
||||
resp_list = self.client.get("/_error/")
|
||||
self.assertEqual(resp_list.status_code, 200)
|
||||
self.assertContains(resp_list, "Центр отладки страниц ошибок")
|
||||
self.assertContains(resp_list, "/_error/404")
|
||||
|
||||
# 2. Предпросмотр конкретной ошибки 404
|
||||
resp_404 = self.client.get("/_error/404")
|
||||
self.assertEqual(resp_404.status_code, 404)
|
||||
self.assertContains(resp_404, "Гипноузел не обнаружен", status_code=404)
|
||||
self.assertContains(resp_404, "СБОЙ МАТРИЦЫ // КОД? 404", status_code=404)
|
||||
|
||||
# 3. Предпросмотр с суффиксом .html
|
||||
resp_404_html = self.client.get("/_error/404.html")
|
||||
self.assertEqual(resp_404_html.status_code, 404)
|
||||
self.assertContains(resp_404_html, "Гипноузел не обнаружен", status_code=404)
|
||||
|
||||
# 4. Несуществующий шаблон ошибки в dev-режиме
|
||||
resp_non_existent = self.client.get("/_error/non-existent-code-999")
|
||||
self.assertEqual(resp_non_existent.status_code, 404)
|
||||
self.assertContains(resp_non_existent, "Шаблон для ошибки 'non-existent-code-999' не найден", status_code=404)
|
||||
|
||||
@@ -19,4 +19,9 @@ urlpatterns = [
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += [path("tmp/", views.tmp, name="web_tmp")]
|
||||
urlpatterns += [
|
||||
path("tmp/", views.tmp, name="web_tmp"),
|
||||
path("_error", views.debug_error_preview, name="debug_error_list"),
|
||||
path("_error/", views.debug_error_preview, name="debug_error_list_slash"),
|
||||
path("_error/<path:code>", views.debug_error_preview, name="debug_error_preview"),
|
||||
]
|
||||
|
||||
@@ -157,6 +157,119 @@ def tmp(request: HttpRequest | None) -> HttpResponse:
|
||||
return render(request, "tmp.html", {})
|
||||
|
||||
|
||||
def debug_error_preview(request: HttpRequest, code: str = "") -> HttpResponse:
|
||||
"""
|
||||
Dev-представление для отладки и предпросмотра страниц ошибок в режиме DEBUG.
|
||||
Поддерживает пути вида /_error/404, /_error/404.html, /_error/500, /_error/, /_error/under_reconstruction.
|
||||
"""
|
||||
from django.template.exceptions import TemplateDoesNotExist
|
||||
from django.template.loader import get_template
|
||||
|
||||
# Очистка имени шаблона от расширения .html и слэшей
|
||||
raw_name = code.strip("/").removesuffix(".html") if code else ""
|
||||
|
||||
available_codes = [
|
||||
"400",
|
||||
"401",
|
||||
"403",
|
||||
"404",
|
||||
"413",
|
||||
"429",
|
||||
"500",
|
||||
"502",
|
||||
"503",
|
||||
"504",
|
||||
"under_reconstruction",
|
||||
]
|
||||
|
||||
if not raw_name:
|
||||
# Индексная страница со списком всех кодов ошибок для dev-отладки
|
||||
links = []
|
||||
for c in available_codes:
|
||||
exists = False
|
||||
for tpl in [f"_error/{c}.html", f"{c}.html"]:
|
||||
try:
|
||||
get_template(tpl)
|
||||
exists = True
|
||||
break
|
||||
except TemplateDoesNotExist:
|
||||
pass
|
||||
status_mark = "готов к просмотру" if exists else "шаблон не создан"
|
||||
links.append(f"<li><a href='/_error/{c}'><strong>/{c}</strong></a> ({status_mark})</li>")
|
||||
|
||||
html = f"""<!doctype html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Dev: Предпросмотр страниц ошибок // HypnoSVG</title>
|
||||
<style>
|
||||
body {{ font-family: ui-monospace, monospace; background: #09090b; color: #d9f99d; padding: 2rem; line-height: 1.6; }}
|
||||
a {{ color: #a3e635; text-decoration: none; border-bottom: 1px dotted #a3e635; }}
|
||||
a:hover {{ border-bottom-style: solid; }}
|
||||
ul {{ list-style-type: square; margin-top: 1rem; }}
|
||||
li {{ margin-bottom: 0.5rem; }}
|
||||
.card {{ background: #18181b; border: 1px solid #27272a; border-radius: 8px; padding: 1.5rem; max-width: 600px; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h2>Центр отладки страниц ошибок (DEBUG=True)</h2>
|
||||
<p>Выберите код для инспекции верстки и интерактивных скриптов перекалибровки:</p>
|
||||
<ul>
|
||||
{''.join(links)}
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
return HttpResponse(html, content_type="text/html; charset=utf-8")
|
||||
|
||||
# Поиск соответствующего шаблона
|
||||
template_candidates = [
|
||||
f"_error/{raw_name}.html",
|
||||
f"{raw_name}.html",
|
||||
f"_error/{raw_name}",
|
||||
raw_name,
|
||||
]
|
||||
|
||||
found_template = None
|
||||
for candidate in template_candidates:
|
||||
try:
|
||||
get_template(candidate)
|
||||
found_template = candidate
|
||||
break
|
||||
except TemplateDoesNotExist:
|
||||
continue
|
||||
|
||||
if not found_template:
|
||||
return HttpResponse(
|
||||
f"<h1>Шаблон для ошибки '{raw_name}' не найден</h1><p>Проверены: {', '.join(template_candidates)}</p><p><a href='/_error/'>← Вернуться к списку</a></p>",
|
||||
status=404,
|
||||
content_type="text/html; charset=utf-8",
|
||||
)
|
||||
|
||||
status_code = 200
|
||||
if raw_name.isdigit() and 400 <= int(raw_name) <= 599:
|
||||
status_code = int(raw_name)
|
||||
|
||||
return render(request, found_template, {}, status=status_code)
|
||||
|
||||
|
||||
def error_400(request: HttpRequest, exception: Exception | None = None) -> HttpResponse:
|
||||
return render(request, "_error/400.html", {}, status=400)
|
||||
|
||||
|
||||
def error_403(request: HttpRequest, exception: Exception | None = None) -> HttpResponse:
|
||||
return render(request, "_error/403.html", {}, status=403)
|
||||
|
||||
|
||||
def error_404(request: HttpRequest, exception: Exception | None = None) -> HttpResponse:
|
||||
return render(request, "_error/404.html", {}, status=404)
|
||||
|
||||
|
||||
def error_500(request: HttpRequest) -> HttpResponse:
|
||||
return render(request, "_error/500.html", {}, status=500)
|
||||
|
||||
|
||||
@require_POST
|
||||
def generate(request: HttpRequest) -> HttpResponse:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user