Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. URL Routing
urls_routing

URL Routing

path, re_path, include, namespace, reverse, get_object_or_404

URL routing в Django

URLconf — исполняемый модуль Python со списком urlpatterns. Django снимает ведущий /, проверяет маршруты сверху вниз и вызывает первую совпавшую view. Поэтому URL — это не только красивый адрес, но и упорядоченный публичный контракт приложения.

Материал актуален для Django 5.2 LTS и 6.0.

#path() и встроенные конвертеры

from django.urls import path from . import views app_name = "articles" urlpatterns = [ path("", views.article_list, name="list"), path("new/", views.article_create, name="create"), path("<slug:slug>/", views.article_detail, name="detail"), ]

Маршрут <slug:slug> передаст view именованный аргумент slug. Статический new/ расположен раньше: встроенный slug также принял бы строку new, и порядок иначе сделал бы форму создания недостижимой.

Встроенные конвертеры:

КонвертерЧто принимает
strнепустую строку без /; это тип по умолчанию
intноль или положительное целое из ASCII-цифр
slugASCII-буквы, цифры, _ и -
uuidUUID в каноническом формате; стандартный маршрут использует строчные hex-буквы
pathнепустую строку, включая /

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

#Когда нужен re_path()

Для обычных ресурсов конвертеры читаются лучше. re_path() нужен, если формат действительно выражается регулярным выражением:

from django.urls import re_path urlpatterns = [ re_path( r"^archive/(?P<year>[0-9]{4})(?:/(?P<month>0[1-9]|1[0-2]))?/$", views.archive, name="archive", ), re_path( r"^download/(?P<name>[^/]+)\.(?P<format>pdf|epub|mobi)$", views.download, name="download", ), ]

В первом шаблоне месяц необязателен вместе с предшествующим /. Именованные группы становятся keyword-аргументами. Если regex содержит и именованные, и неименованные группы, Django передаёт только именованные — смешивать стили не стоит.

re_path() не является «старым path». Он актуален, просто регулярное выражение сложнее читать, обращать через reverse() и тестировать.

#Разделяйте URLconf по приложениям

Корневой модуль задаёт крупные префиксы:

# config/urls.py from django.contrib import admin from django.urls import include, path urlpatterns = [ path("admin/", admin.site.urls), path("articles/", include("articles.urls")), path("accounts/", include("django.contrib.auth.urls")), ]

Приложение хранит собственные относительные маршруты и app_name. Получаются имена articles:list, articles:create и articles:detail. Namespace позволяет двум приложениям иметь маршрут detail без конфликта.

Глубоко вложенные списки include([...]) технически возможны, но отдельные urls.py обычно проще навигации и тестирования.

#Стройте URL по имени

reverse() отделяет вызывающий код от конкретной строки маршрута:

from django.urls import reverse detail_url = reverse( "articles:detail", kwargs={"slug": "django-orm"}, )

В шаблоне используется тот же контракт:

<a href="{% url 'articles:detail' slug=article.slug %}"> {{ article.title }} </a>

redirect("articles:detail", slug=article.slug) сначала делает reverse, затем возвращает перенаправление. Жёсткая строка "/articles/" допустима для внешнего адреса или специального случая, но внутренние маршруты лучше именовать.

#Query string и fragment: актуально с Django 5.2

reverse() принимает query и fragment, поэтому кодирование параметров не нужно писать вручную:

url = reverse( "articles:list", query={"page": 2, "tag": ["django", "orm"]}, fragment="results", ) # /articles/?page=2&tag=django&tag=orm#results

Это корректно сохраняет повторяющиеся параметры и экранирует значения. В legacy-коде встречается f-строка с пользовательским вводом в query string; её заменяют на reverse(..., query=...) либо urllib.parse.urlencode(..., doseq=True) для Django до 5.2.

#reverse() и reverse_lazy()

Обычный reverse() вычисляется немедленно. В атрибуте класса URLconf может быть ещё не загружен, поэтому generic view использует ленивый вариант:

from django.urls import reverse_lazy from django.views.generic import DeleteView class ArticleDeleteView(DeleteView): model = Article success_url = reverse_lazy("articles:list")

Внутри get_success_url() маршрутизация уже готова, и обычный reverse() понятнее.

#Область видимости объекта входит в запрос

get_object_or_404() принимает модель, Manager или QuerySet:

from django.shortcuts import get_object_or_404, render def article_detail(request, slug): article = get_object_or_404( Article.objects.published().select_related("author"), slug=slug, ) return render(request, "articles/detail.html", {"article": article})

Функция преобразует DoesNotExist в Http404, но не проверяет права автоматически. Политика доступа выражена переданным QuerySet. MultipleObjectsReturned не превращается в 404 — уникальность slug должна обеспечиваться схемой базы.

get_list_or_404() возвращает список и выдаёт 404 при пустой выборке. Для обычного каталога пустая страница часто нормальна, поэтому этот shortcut нужен реже.

#Кастомный конвертер должен работать в обе стороны

Конвертер участвует и в распознавании URL, и в reverse():

class MonthConverter: regex = r"0[1-9]|1[0-2]" def to_python(self, value): return int(value) def to_url(self, value): month = int(value) if not 1 <= month <= 12: raise ValueError("month must be between 1 and 12") return f"{month:02d}"
from django.urls import path, register_converter from .converters import MonthConverter register_converter(MonthConverter, "month") urlpatterns = [ path("archive/<int:year>/<month:month>/", views.archive, name="month"), ]

to_python() преобразует фрагмент входящего пути. to_url() преобразует значение при reverse и должен отклонять неподходящее значение через ValueError. Regex и обе функции должны описывать один домен.

#Проверяйте маршрутизацию тестами

resolve() сообщает, какая view соответствует пути, а reverse() проверяет обратное направление:

from django.test import SimpleTestCase from django.urls import resolve, reverse class ArticleUrlsTests(SimpleTestCase): def test_detail_round_trip(self): url = reverse("articles:detail", kwargs={"slug": "django-orm"}) match = resolve(url) self.assertEqual(match.view_name, "articles:detail") self.assertEqual(match.kwargs, {"slug": "django-orm"})

Добавьте тесты конфликтных статических слов вроде new, допустимых границ конвертера и завершающего slash. Это дешевле, чем узнавать о затенённом маршруте из production.

#Завершающий slash

Проект должен выбрать последовательный стиль. При CommonMiddleware и APPEND_SLASH=True Django может перенаправить адрес без slash на вариант со slash, если первый не совпал. Для изменяющего POST такое перенаправление не следует считать способом исправления клиента: адрес формы и API должен быть корректным сразу.

#Media и static в разработке

Helper static() можно использовать для пользовательских media только в локальном DEBUG-режиме:

from django.conf import settings from django.conf.urls.static import static if settings.DEBUG: urlpatterns += static( settings.MEDIA_URL, document_root=settings.MEDIA_ROOT, )

Это не production file server. Статику django.contrib.staticfiles обслуживает runserver в разработке; в production файлы собирают collectstatic и отдают web server/CDN/storage по выбранной архитектуре.

Syndication feed также подключается как view-объект, а не через include("django.contrib.syndication.views"):

path("feed/", LatestEntriesFeed(), name="feed")

#Legacy и удалённый API

django.conf.urls.url() удалён в Django 4.0. Его замена — path() для конвертеров или re_path() для regex. Сам re_path() не legacy.

Функции django.core.urlresolvers.reverse из старых проектов перенесены в django.urls ещё в Django 2.0. При поддержке legacy ищите также жёстко заданные внутренние адреса и ручную сборку query string: они часто не падают сразу, но ломаются при смене префикса или специальных символах.

#Документация

  • URL dispatcher
  • URL utility functions
  • Shortcuts

Далее: Шаблоны: DTL