← Назад к вопросам

Как происходит обработка исключений в Django?

2.2 Middle🔥 161 комментариев
#Django

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как происходит обработка исключений в Django

Django имеет многоуровневую систему обработки исключений, которая перехватывает ошибки на разных этапах обработки запроса. Это позволяет приложению оставаться стабильным и предоставлять пользователям полезную информацию об ошибке.

1. Жизненный цикл обработки исключения

Когда происходит исключение, Django проходит следующие этапы:

Запрос → Middleware (in) → View → Middleware (out) → Response
         ↓
    Исключение возникает где-то
         ↓
    Exception Middleware перехватывает
         ↓
    Django выбирает обработчик
         ↓
    Возвращает ошибку пользователю

2. Middleware обработка исключений

Django проходит через middleware в обратном порядке при исключении:

# Порядок middleware в settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    # Ваши middleware
]

# Пример custom middleware для перехвата исключений
from django.http import JsonResponse

class ExceptionHandlingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        try:
            response = self.get_response(request)
        except ValueError as e:
            return JsonResponse(
                {"error": "Invalid value", "details": str(e)},
                status=400
            )
        except Exception as e:
            return JsonResponse(
                {"error": "Internal server error"},
                status=500
            )
        return response

3. Встроенные исключения Django

Django предоставляет специальные исключения для разных случаев:

from django.http import Http404
from django.shortcuts import get_object_or_404
from django.core.exceptions import ValidationError, PermissionDenied

# Http404 - автоматически возвращает 404
from django.views import View

class DetailView(View):
    def get(self, request, id):
        # Вариант 1: автоматический 404
        post = get_object_or_404(Post, id=id)
        
        # Вариант 2: явный выброс
        if not post:
            raise Http404("Post not found")
        
        return render(request, 'post.html', {'post': post})

# PermissionDenied - 403
class AdminView(View):
    def get(self, request):
        if not request.user.is_staff:
            raise PermissionDenied("Access denied")
        return render(request, 'admin.html')

# ValidationError для валидации
from django.core.exceptions import ValidationError

def validate_age(age):
    if age < 18:
        raise ValidationError("Age must be at least 18")

4. Обработчики ошибок (Handler Views)

Django ищет специальные view функции для различных кодов ошибок:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

# Обработчики ошибок (обычно в главном urls.py)
handler400 = 'myapp.views.bad_request'
handler403 = 'myapp.views.permission_denied'
handler404 = 'myapp.views.page_not_found'
handler500 = 'myapp.views.server_error'

# views.py
def page_not_found(request, exception):
    return render(request, '404.html', status=404)

def server_error(request):
    return render(request, '500.html', status=500)

5. Сигналы для исключений

Django отправляет сигналы при возникновении исключений:

from django.core.signals import request_started, request_finished
from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(request_started)
def log_request_start(sender, **kwargs):
    print(f"Request started: {kwargs['request'].path}")

@receiver(request_finished)
def log_request_finish(sender, **kwargs):
    print(f"Request finished")

6. Логирование исключений

Django встроенно логирует исключения:

# settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'ERROR',
            'class': 'logging.FileHandler',
            'filename': 'errors.log',
        },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file', 'console'],
            'level': 'ERROR',
        },
    },
}

# В views
import logging
logger = logging.getLogger('django')

def risky_view(request):
    try:
        result = 10 / 0
    except ZeroDivisionError as e:
        logger.error(f"Math error: {e}", exc_info=True)
        raise

7. DEBUG режим

В DEBUG=True Django показывает подробную информацию об ошибке:

# settings.py
DEBUG = True  # На production ВСЕГДА False!
ALLOWED_HOSTS = ['*']  # На production указать конкретные хосты

Этот режим:

  • Показывает full stack trace
  • Раскрывает переменные и их значения
  • Показывает SQL запросы
  • НИКОГДА не использовать на production (утечка информации)

8. Custom исключения

class CustomException(Exception):
    """Базовое пользовательское исключение"""
    pass

class InsufficientFundsException(CustomException):
    """Недостаточно средств"""
    def __init__(self, available, required):
        self.available = available
        self.required = required
        super().__init__(f"Need {required}, have {available}")

# Использование
try:
    if balance < amount:
        raise InsufficientFundsException(balance, amount)
except InsufficientFundsException as e:
    logger.error(f"Payment failed: {e}")
    return JsonResponse({"error": str(e)}, status=402)

9. Декоратор для обработки исключений

from functools import wraps

def handle_exceptions(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        try:
            return view_func(request, *args, **kwargs)
        except Http404:
            return render(request, '404.html', status=404)
        except PermissionDenied:
            return render(request, '403.html', status=403)
        except ValidationError as e:
            return JsonResponse({"errors": e.message_dict}, status=400)
        except Exception as e:
            logger.exception("Unexpected error")
            return render(request, '500.html', status=500)
    return wrapper

@handle_exceptions
def my_view(request):
    return render(request, 'page.html')

10. Context processors для ошибок

# context_processors.py
def error_handler(request):
    return {
        'error': request.GET.get('error'),
        'show_debug_info': settings.DEBUG,
    }

# settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'OPTIONS': {
            'context_processors': [
                'myapp.context_processors.error_handler',
            ],
        },
    },
]

Заключение

Django обеспечивает многоуровневую защиту от исключений: middleware, специальные view функции для ошибок, встроенные исключения для стандартных ситуаций, и логирование. Ключ к надёжному приложению — правильная обработка исключений на каждом уровне, использование специальных исключений Django, и логирование с полезной информацией для отладки.

Как происходит обработка исключений в Django? | PrepBro