Жизненный цикл запроса и middleware

Чтобы по-настоящему понять Django, нужно проследить жизнь одного запроса от клика в браузере до готовой страницы. Это «скелет», на который нанизывается всё остальное.
Суть: запрос проходит через сервер, middleware и URL-резолвер, попадает в view, которая собирает данные и возвращает HttpResponse; ответ снова идёт через middleware к клиенту.

Полный жизненный цикл запроса

Когда пользователь открывает example.com/blog/42/, происходит длинная, но строго упорядоченная цепочка событий. Понимание этого порядка избавит вас от половины будущих багов — вы будете знать, где именно «ломается» запрос.

1. Браузер шлёт GET /blog/42/
        │
2. WSGI/ASGI-сервер принимает соединение
        │
3. Middleware (вниз):
     SecurityMiddleware → SessionMiddleware
     → AuthenticationMiddleware → CsrfViewMiddleware
        │
4. URLconf: /blog/42/ совпал с path("blog/<int:pk>/")
        │
5. View вызывается: post_detail(request, pk=42)
        │           ┌─────────────┐
        ├──────────▶│  ORM/Model  │  SELECT ... WHERE id=42
        │           └─────────────┘
6. View рендерит template с данными
        │
7. Возвращается HttpResponse(200, html)
        │
8. Middleware (вверх, обратный порядок)
        │
9. Сервер отдаёт ответ браузеру

Объект request

В каждую view Django передаёт объект HttpRequest — это «конверт» со всей информацией о запросе. Самые важные его атрибуты: request.method (GET, POST), request.GET и request.POST (данные форм и query-параметры), request.user (текущий пользователь), request.path (путь). Простейшая view выглядит так:

from django.http import HttpResponse

def hello(request):
    name = request.GET.get("name", "гость")
    return HttpResponse(f"Привет, {name}!")

Откройте /?name=Аня — и в ответ прилетит «Привет, Аня!». Это и есть атом Django: функция, которая берёт request и возвращает response.

Middleware: слои-обёртки

Middleware — это список обработчиков, через которые проходит каждый запрос «вниз», а каждый ответ — «вверх». Они объявлены в settings.py в списке MIDDLEWARE. Именно middleware незаметно подключает куку сессии, определяет request.user и проверяет CSRF-токен у POST-запросов. Порядок в списке важен: каждый слой оборачивает следующий, как матрёшка.

Как это работает под капотом

По сути URL-резолвер — это перебор правил: он идёт по списку маршрутов сверху вниз и берёт первый подходящий шаблон, извлекая из URL именованные части (как pk=42). Это языко-независимая логика, которую легко смоделировать на чистом Python:

# Попробуй сам ▶ — мини-роутер в духе Django URLconf
import re

routes = [
    (r"^/$",                "home"),
    (r"^/blog/$",           "post_list"),
    (r"^/blog/(\d+)/$",     "post_detail"),
    (r"^/about/$",          "about"),
]

def resolve(path):
    for pattern, view in routes:
        m = re.match(pattern, path)
        if m:
            return view, m.groups()
    return "404_not_found", ()

for url in ["/", "/blog/", "/blog/42/", "/missing/"]:
    view, args = resolve(url)
    print(f"{url:14} -> {view} {args}")

Django делает то же самое, только мощнее: разбирает типы (int, slug, uuid), именует группы и кеширует разбор. Но принцип — «первый подошедший шаблон выигрывает» — ровно такой же.

GET против POST

GET-запрос запрашивает данные и не должен менять состояние сервера (открыть страницу, поискать). POST отправляет данные, которые что-то меняют (создать пост, залогиниться). Это не просто соглашение: браузеры кешируют GET, повторяют его при «назад», а POST требует подтверждения. View почти всегда проверяет метод и ветвится по нему.

Частые ошибки

  • Менять данные в GET-запросе. Удаление по GET-ссылке выполнят поисковые роботы и префетч браузера.
  • Забыть про порядок middleware. Если переставить слои, аутентификация или CSRF могут перестать работать.
  • Читать request.POST при GET-запросе. Он будет пустым — всегда проверяйте request.method.
  • Не возвращать HttpResponse. View обязана что-то вернуть, иначе будет ошибка.

Best practices

  • Держите views тонкими: логику выносите в модели и сервисы.
  • Используйте request.GET.get("ключ", default), чтобы не падать на отсутствующем параметре.
  • Соблюдайте семантику HTTP-методов — это влияет на кеширование и безопасность.

Итоги

Жизненный цикл запроса — сервер, middleware, URLconf, view, ответ. Объект request несёт всё о запросе, HttpResponse — всё об ответе. URL-резолвер выбирает первый подходящий маршрут. Этот скелет лежит в основе всех следующих разделов.

Проверьте себя
1. В каком порядке запрос проходит через middleware и ответ возвращается?
AЗапрос и ответ — оба сверху вниз
BЗапрос вниз по списку, ответ вверх в обратном порядке
CПорядок случайный
DMiddleware не участвует в ответе
2. Почему нельзя удалять данные через GET-запрос?
AGET медленнее POST
BGET кешируется и может выполниться роботами или префетчем браузера
CGET не поддерживает русские буквы
DDjango запрещает GET