Как Django сопоставляет URL-адрес запроса с соответствующим обработчиком (view)?

Ответ

Django обрабатывает URL-адреса с помощью механизма URL-диспетчера (URL dispatcher). Его основная задача — сопоставить запрошенный URL с конкретной функцией или классом-представлением (view), который сгенерирует ответ.

Процесс состоит из следующих шагов:

  1. Входная точка: Веб-сервер передает запрос Django. Django определяет корневой файл URL-конфигурации (обычно urls.py в папке проекта), указанный в настройке ROOT_URLCONF.
  2. Поиск по urlpatterns: Django последовательно перебирает все шаблоны в списке urlpatterns в этом файле.
  3. Первое совпадение: Как только находится первый шаблон, соответствующий запрошенному URL, поиск прекращается.
  4. Вызов View: Django вызывает связанную с этим шаблоном view-функцию, передавая ей объект HttpRequest и любые параметры, извлеченные из URL (например, year из articles/<int:year>/).
  5. Генерация ответа: View-функция обрабатывает запрос и возвращает объект HttpResponse.

Пример urls.py:

from django.urls import path
from . import views # views.py в том же приложении

urlpatterns = [
    # URL: /articles/
    path('articles/', views.article_list, name='article-list'),

    # URL: /articles/2023/
    # <int:year> - это "конвертер пути", который захватывает целое число
    # и передает его в view как аргумент `year`
    path('articles/<int:year>/', views.article_archive, name='article-archive-year'),
]

Ключевые особенности системы:

  • Конвертеры путей (Path Converters): Использование int:, slug:, str: для автоматического захвата и преобразования типов параметров из URL.
  • Обратный вызов URL (Reverse URL resolving): Использование имен (name='article-list') для генерации URL в шаблонах и коде ({% url 'article-list' %}), что делает код более гибким и устойчивым к изменениям.
  • Вложенные urlpatterns: Возможность подключать файлы urls.py из отдельных приложений с помощью функции include(), что способствует модульности.
  • Регулярные выражения: Для сложных шаблонов можно использовать re_path() вместо path().

Ответ 18+ 🔞

А, ну это же про то, как Джа́нго, этот упоротый фреймворк, решает, куда какой запрос послать! Слушай, тут всё просто, если не заморачиваться.

Представь себе, что твой сайт — это такой огромный, блядь, сортировочный центр для писем, а URL dispatcher — это главный дед-начальник, который смотрит на адрес на конверте и орёт: «Эту папку — в третий кабинет, а эту хуйню с цифрами — Иванову на обработку!».

Вот как этот цирк работает, по шагам:

  1. Входная дверь. Веб-сервер приносит конверт с адресом (URL) в приёмную Джа́нго. Джа́нго сразу лезет в главный справочник (ROOT_URLCONF) и находит там основной файл правил — обычно это urls.py в папке проекта. Это типа устав сортировочного центра.
  2. Чтение устава. Джа́нго открывает этот файл и видит там священный список под названием urlpatterns. Это просто перечень всех возможных адресных шаблонов и указаний, что с ними делать.
  3. Поиск совпадения. Он начинает сверять адрес на конверте с каждым шаблоном в списке, сверху вниз. Как только находит первое, блядь, совпадение — всё, поиск окончен. Остальные шаблоны он даже не смотрит, ленивая жопа.
  4. Передача дела. Нашёл шаблон — увидел, какая функция-обработчик (view) к нему привязана. Джа́нго вызывает эту функцию, суёт ей в руки сам конверт-запрос (HttpRequest) и все вытащенные из адреса параметры (типа год, номер статьи — об этом ниже).
  5. Получение ответа. Функция там внутри что-то делает, думает, и в конце концов выплёвывает готовый ответ (HttpResponse) — HTML-страницу, JSON или просто «404, иди нахуй».

Вот смотри, как это выглядит в коде (urls.py):

from django.urls import path
from . import views  # Импортируем наши функции-обработчики из соседнего файла

urlpatterns = [
    # Если пришёл запрос на /articles/ — зови функцию views.article_list
    path('articles/', views.article_list, name='article-list'),

    # А вот это интереснее! Если запрос типа /articles/2024/
    # <int:year> — это конвертер пути. Он выцепит из URL число (2024),
    # проверит, что это действительно int, и передаст его в функцию
    # как аргумент с именем `year`. Удобно, ёпта!
    path('articles/<int:year>/', views.article_archive, name='article-archive-year'),
]

А теперь про крутые фишки, без которых жить нельзя:

  • Конвертеры путей (Path Converters): Это вот эти штуки <int:year>, <slug:post_slug>. Они не просто кусок текста из адреса вырывают, а ещё и приводят его к нужному типу. int: — к числу, slug: — к строке без пробелов и спецсимволов. Если в int: попадёт буква — Джа́нго просто скажет «не нашёл» и пойдёт дальше по списку. Красота!
  • Обратный поиск URL (Reverse resolving): Ты даёшь каждому пути имя (name='article-list'). А потом в шаблонах или коде просто говоришь: «Джа́нго, дай мне URL для 'article-list'». И он тебе его сам сгенерирует! Поменял путь в urls.py — и он автоматически везде обновится. Это овердохуище удобно, чтобы не плодить хардкод.
  • Вложенность (include): Чтобы в главном urls.py не было пиздеца на тысячу строк, можно подключать urlpatterns из отдельных приложений. Типа path('blog/', include('blog.urls')). Получается модульно и чисто.
  • Регулярки (re_path): Если твои шаблоны адресов настолько ебнутые, что стандартными path() не описать — всегда можно, блядь, выкатить тяжёлую артиллерию в виде регулярных выражений через re_path(). Но это уже для извращенцев.

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