Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

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

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

Каждый образец состоит из описания статических и динамических частей пути в виде строки или регулярного выражения. Статические части пути в образце просто проверяются на равенство соответствующим участкам пути в запросе. Динамические же участки пути позволяют захватывать (capture) значения и передавать во view в качестве аргументов.

Как только выясняется, что путь или его начало совпали с образцом, происходит либо вызов view, либо передача оставшейся части пути во вложенный блок urlpatterns. В большинстве больших Django-проектов 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 уже не требуется какая-либо обработка пути (хотя это и возможно).

Давайте опишем urlpattern для примера пути, приведённого выше:

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

Здесь <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 одного приложения — стоит воспользоваться возможностью включения (inclusion) одних 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. Сделайте так, чтобы hello_django.calc.views.index принимала целочисленные параметры "a" и "b" из пути /calc/A/B и выводила текст 40 + 2 = 42 (с указанными числами в качестве значений, само собой!).
  2. Назначьте hello_django.calc.views.index имя "calc".
  3. Сделайте так, чтобы открытие "домашней страницы" делало перенаправление на /calc/40/2. Для этого используйте django.shortcuts.redirect и django.urls.reverse. Не задавайте URL напрямую, используйте именно обратный маршрут!

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

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

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

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.

Об обучении на Хекслете

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

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

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

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

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

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

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

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
1 июня 10 месяцев

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

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

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

Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»