Зарегистрируйтесь, чтобы продолжить обучение

Маршрутизация Python: Разработка на фреймворке Django

Важнейшей частью любого веб-фреймворка является механизм, который отвечает за маршрутизацию. Во Flask для построения карты маршрутов использовались специальные декораторы. В Django для этого используется свой небольшой eDSL. Он описывает urlpatterns — набор образцов, с которыми сопоставляются пути из каждого входящего запроса.

Каждый образец состоит из описания статических и динамических частей пути в виде строки или регулярного выражения:

  • Статические части пути в образце просто проверяются на равенство соответствующим участкам пути в запросе
  • Динамические участки пути позволяют захватывать значения и передавать во view в качестве аргументов

Как только выясняется, что путь или его начало совпали с образцом, происходит либо вызов view, либо передача оставшейся части пути во вложенный блок urlpatterns. В большинстве больших Django-проектов urlpatterns вложены друг в друга и представляют собой дерево.

В этом уроке подробно разберем статические и динамические маршруты, а также рассмотрим вложенные urlpatterns и обратные маршруты.

Статические маршруты

Мы с вами уже описали один статический маршрут:

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

Здесь path сопоставляет образец '' с вьюхой views.index. Образец «пустая строка» соответствует пустому пути — запросам главной страницы сайта. Любой не пустой путь не совпадет с таким образцом. Статические образцы обычно описываются строками вида fruits/apples/golden_one и ожидают запросов строго по этому же пути.

Имя домена не фигурирует в urlpatterns, что позволяет размещать одно и то же приложение на любом домене.

Динамические маршруты

Авторы Django — сторонники использования читаемых URL. Это означает, что маршруты в Django-приложениях выглядят так, что понятно, куда ведет путь. Например, по пути /users/42/pets/101/med_info/ можно догадаться, что запрашивается медицинская информация (med_info) для питомца с идентификатором 101 (pets/101). Он принадлежит пользователю с идентификатором 42 (user/42).

Иногда получается пойти дальше и вместо идентификаторов использовать имена. Например, такое возможно для имен пользователей, которые обычно уникальны в пределах системы. URL при использовании имен может выглядеть так: /users/~bob/books/.

Пути, которые включают в себя данные — идентификаторы и имена — называются динамическими. И динамические маршруты используются как раз с такими путями.

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

Опишем urlpatterns для примера пути, который приведен выше:

## urls.py

urlpatterns = [
    path('users/<int:user_id>/pets/<int:pet_id>/med_info/', med_info_view),
    
]
## views.py

def med_info_view(request, user_id, pet_id):
    ...

Здесь <int:XXX> означает ту самую динамическую часть пути. int означает, что в этом участке пути ожидается целое число в виде строки. Если сервер получит запрос по пути /users/42/pets/101/med_info/, маршрутизация закончится вызовом вью med_info_view(request, user_id=42, pet_id=101).

Кроме int Django предоставляет и другие преобразователи путей — path converters. Более того, можно определять и собственные. А если пути специфические, то всегда можно использовать регулярные выражения, чтобы выделить интересные нам части пути.

Вложенные urlpatterns

Иногда маршрутов становится слишком много и среди них намечаются группы, у которых общая статическая часть. Например, это маршруты ко views одного приложения. В этом случае стоит воспользоваться возможностью включения одних urlpatterns в другие.

Предположим, у нас в проекте есть приложение project.users, в котором все views находятся под общим префиксом /users/. Нам достаточно создать модуль project.users.urls с описанием urlpatterns уже без префикса и подключить модуль в корневой project.urls:

# project.users.urls
from django.urls import path

from project.users import views

urlpatterns = [
    path('', views.users_view),
    path('<int:user_id>/pets/<int:pet_id>/med_info/', views.pet_med_info_view),
    
]
# project.urls
from django.urls import path, include

urlpatterns = [
    
    path('users/', include('project.users.urls')),
    
]

В новом наборе urlpatterns у образцов нет префикса users. А в основном urlpatterns указано, что все пути, которые начинаются с users, нужно сопоставлять с образцами из project.users.urls.

Мы подключили вложенные urlpatterns с помощью функции django.urls.include и указали модуль в виде строки. Можно импортировать модуль и указать вместо цели маршрута сразу его: path('users/', project.users.urls) — эти два варианта эквивалентны. Но неявное подключение вместо импорта решает одну важную задачу: избавляет от потенциальных циклических импортов.

Ранее мы закомментировали в нашем мини-проекте строчку path('admin/', admin.site.urls). Это тоже включение админки в нашу карту маршрутов по префиксу admin. Подобным образом в приложение часто подключаются сторонние пакеты, у которых собственные маршруты.

Обратные маршруты или reverse

Часто нужно получить для определенного маршрута правильный путь. Например, необходимо кому-то дать ссылку на медицинскую карточку питомца пользователя. Если мы будем вручную собирать путь из строк, то при изменениях в маршруте новый путь может стать некорректным.

Чтобы была возможность для любого маршрута всегда получить правильный путь, нужно произвести операцию, обратную маршрутизации — у Django есть функции reverse и reverse_lazy. Они позволяют получить путь по имени маршрута. Поэтому маршруты, которые нужно обращать, необходимо поименовать (задать уникальное имя):

urlpatterns = [
    
    path(
        '<int:user_id>/pets/<int:pet_id>/med_info/',
        views.pet_med_info_view,
        name='pet_med_info',  # <--- задаем имя маршруту
    ),
    
]

Когда маршрут поименован, можно получить путь вызовом вида reverse('pet_med_info', kwargs={'user_id': 42, 'pet_id': 101}). Как бы ни менялась маршрутизация в дальнейшем, пока путь содержит те же именованные участки и назван по-старому, эта функция будет давать актуальный для маршрута путь.

Функция reverse_lazy нужна, когда путь нужно получить на этапе инициализации программы, например, при описании class based views. Во время инициализации путь может потребоваться до того, как вся карта маршрутов будет построена. И тут функция reverse_lazy всего лишь оставляет обещание вернуть путь, когда он реально понадобится — когда сервер уже начнет отвечать на запросы.

В итоге reverse_lazy используем в атрибутах классов, а в теле вьюх и шаблонах — reverse. Последняя работает быстрее, но только с готовой картой маршрутов.


Самостоятельная работа

  1. Сделайте так, чтобы hexlet_django_blog.article.views.index принимала строковый параметр "tags" и целочисленный параметр "article_id" из пути /articles/tags/article_id и выводила текст в виде Статья номер 42. Тег python
  2. Назначьте hexlet_django_blog.article.views.index имя "article"
  3. Сделайте так, чтобы открытие "домашней страницы" делало перенаправление на /articles/python/42. Для этого используйте django.shortcuts.redirect и django.urls.reverse. Не задавайте URL напрямую, используйте обратный маршрут

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Программирование на Python, Разработка веб-приложений и сервисов используя Django, проектирование и реализация REST API
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»