Динамические URL. Пользовательские конвертеры

Курс по Django: https://stepik.org/a/183363

Архив проекта: 06_sitewomen.zip

Сейчас в нашем проекте маршруты приложения women подключаются с помощью функции include:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('women.urls')),
]

Соответственно, за главную страницу отвечает функция представления index, а за вывод разделов – функция categories. Но у нас может быть множество категорий и хотелось бы, например, их отображать с помощью таких URL:

127.0.0.1:8000/cats/1/

127.0.0.1:8000/cats/2/

127.0.0.1:8000/cats/3/

...

Как прописать такой URL-шаблон в Django? Для этого в списке адресов приложения следует указать числовой параметр. Это можно сделать следующим образом (в файле women/urls.py):

urlpatterns = [
    path('', views.index),
    path('cats/<int:cat_id>/', views.categories),
]

Смотрите, здесь в угловых скобках записан параметр cat_id, который имеет тип int – целочисленный. Этот тип в маршрутах называется конвертером. И указанный путь будет соответствовать любым комбинациям URL с фрагментом ‘cats/число/’. Далее, в функции представления categories мы уже можем использовать этот параметр:

def categories(request, cat_id):
    return HttpResponse(f"<h1>Статьи по категориям</h1><p >id:{cat_id}</p>")

Как видите, все предельно просто. Помимо конвертера int в Django можно использовать и другие:

  • str – любая не пустая строка, исключая символ ‘/’;
  • int – любое положительное целое число, включая 0;
  • slug – слаг, то есть, латиница ASCII таблицы, цифры, символы дефиса и подчеркивания;
  • uuid – цифры, малые латинские символы ASCII, дефис;
  • path – любая не пустая строка, включая символ ‘/’.

https://docs.djangoproject.com/en/4.2/topics/http/urls/

Например, мы можем добавить еще один маршрут для доступа к категориям через слаг (slug) следующим образом:

path('cats/<slug:cat_slug>/', views.categories_by_slug),

Изменим функцию представления:

def categories_by_slug(request, cat_slug):
    return HttpResponse(f"<h1>Статьи по категориям</h1><p >slug:{ cat_slug }</p>")

И теперь в качестве категории можно указывать не только числа, но и строки в виде слага, например, так:

127.0.0.1:8000/cats/3/

127.0.0.1:8000/cats/sport/

127.0.0.1:8000/cats/music/

Как видите, слаги понятнее для пользователя и поисковых систем. Сайты с такими URL, в среднем, лучше индексируются и занимают более высокие позиции в поисковой выдаче. Причем, когда передается число, то отрабатывает первый маршрут из URL с числовым конвертером, если же числовой шаблон не подходит, то проверяется следующий со слагом. И здесь очень важен порядок. Если поменять и записать сначала конвертер со слагом, а затем, с числом:

urlpatterns = [
    path('', views.index),
    path('cats/<slug:cat_slug>/', views.categories_by_slug),
    path('cats/<int:cat_id>/', views.categories),
]

то всегда будет срабатывать слаг, так как число – это частный случай слага. На это обращайте внимание: сначала следует прописывать частные конвертеры, а затем, все более общие.

Если по каким-то причинам представленных типов для URL недостаточно, то в Django имеется функция

re_path()

которая делает все то же самое, но с использованием регулярных выражений. Например, определим URL, в котором можно указывать год в виде четырех чисел:

re_path(r'^archive/(?P<year>[0-9]{4})/', views.archive),

Здесь year – это имя переменной, которая будет содержать введенный год.

Добавим функцию представления archive:

def archive(request, year):
    return HttpResponse(f"<h1>Архив по годам</h1><p >{year}</p>")

И теперь мы можем обращаться к этому URL, указывая ровно 4 цифры:

http://127.0.0.1:8000/archive/2020/

Если же указать меньше или больше чисел, то шаблон URL не совпадет и возникнет исключение 404 – страница не найдена. Вот так, достаточно гибко можно задавать список URL для механизма маршрутизации в Django.

Создание собственных конвертеров

Фреймворк Django позволяет создавать свои собственные конвертеры, используемые при анализе URL-адресов. Например, вместо того чтобы использовать функцию re_path() с регулярным выражением, мы можем создать конвертер, который бы выделял и проверял год в URL-адресе. Подробная документация приведена на следующей странице:

https://docs.djangoproject.com/en/4.2/topics/http/urls/

В частности, в разделе «Registering custom path converters» приведен пример класса FourDigitYearConverter с регулярным выражением для выделения четырех цифр:

class FourDigitYearConverter:
    regex = "[0-9]{4}"
 
    def to_python(self, value):
        return int(value)
 
    def to_url(self, value):
        return "%04d" % value

Кроме того, у него имеются два метода:

  • to_python() – для преобразования фрагмента URL в требуемый тип данных;
  • to_url() – для преобразования параметра в формат, требуемый для представления в URL.

В частности, в классе FourDigitYearConverter в методе to_python() параметр value преобразуется в целое число (год), а в методе to_url() обратно в строку из четырех цифр.

Нам вполне подходит этот класс, поэтому я его просто скопирую и добавлю в приложение women в файл converters.py. Затем, перейдем в файл urls.py приложения women и зарегистрируем этот конвертер с именем year4 для использования в URL-шаблонах:

register_converter(converters.FourDigitYearConverter, "year4")

Далее, в коллекции urlpatterns вместо функции re_path() можно прописать:

path('archive/<year4:year>/', views.archive),

При этом параметр year будет автоматически преобразован в целое число благодаря методу to_python() класса FourDigitYearConverter. В этом легко убедиться, поставив точку останова в функции представления archive() и запустив Django в режиме отладки.

Курс по Django: https://stepik.org/a/183363

Видео по теме