Динамические URL и конвертеры

URL может содержать переменные части: /user/42, /post/hello-world. Flask вытаскивает их из пути и передаёт в view-функцию как аргументы.
Динамический маршрут — это шаблон с «дыркой»: /user/<int:user_id>. Любой подходящий путь матчится, а значение из дырки приезжает в функцию параметром. Конвертер задаёт тип и форму этой дырки.

Жёстко прописывать каждый URL невозможно — пользователей миллионы. Нужен шаблон: «после /user/ идёт число — это id». Flask описывает переменную часть в угловых скобках, а имя внутри становится именем параметра функции.

@app.route("/user/<int:user_id>")
def show_user(user_id):
    return f"Пользователь №{user_id}"

@app.route("/post/<slug>")
def show_post(slug):
    return f"Статья: {slug}"

Конвертер перед двоеточием задаёт тип. Основные: string (по умолчанию, без слэшей), int (целое), float, path (строка со слэшами), uuid. Конвертер не просто кастует тип — он ещё фильтрует: <int:id> не совпадёт с /user/abc, и Flask вернёт 404, не дойдя до функции. Это бесплатная валидация на уровне маршрута.

@app.route("/files/<path:filepath>")   # ловит и docs/intro.txt
def serve(filepath):
    return f"Файл: {filepath}"

Конвертеры — это маленькая, но недооценённая защита. Они не просто кастуют тип: они отсекают мусорные запросы ещё до твоего кода, экономя обработку и устраняя целый класс ошибок «а вдруг там не число». Выбирая между голым id и человекочитаемым slug, думай о SEO и пользователе: /post/42 ничего не говорит, /post/kak-rabotaet-flask и читается, и лучше индексируется. Часто используют гибрид — и slug, и id в URL. И ещё нюанс: порядок объявления маршрутов имеет значение, когда шаблоны могут пересекаться; более общий path-маршрут способен «перехватить» запросы, предназначенные конкретным путям, поэтому располагай специфичные правила раньше широких.

Как работает под капотом

Каждое правило компилируется в регулярное выражение. Конвертер int даёт паттерн вроде \d+, string — «всё кроме слэша». При запросе Werkzeug прогоняет путь через эти регулярки, и первое совпавшее правило выигрывает; захваченные группы становятся аргументами функции.

  /user/42
     │
     ▼
  правило "/user/<int:user_id>"
  компилируется в  ^/user/(\d+)$
     │  совпало, группа = "42"
     ▼
  конвертер int: "42" → 42 (тип int)
     │
     ▼
  show_user(user_id=42)

Если бы пришло /user/abc, регулярка \d+ не совпала бы — Flask вернул бы 404 ещё до вызова функции. Смоделируем эту проверку и приведение типа вручную.

import re

def match_route(pattern_regex, converter, path):
    m = re.match(pattern_regex, path)
    if not m:
        return ("404", None)
    raw = m.group(1)
    value = converter(raw)
    return ("200", value)

# имитируем правило /user/<int:user_id>
rx = r"^/user/(\d+)$"
for p in ["/user/42", "/user/abc", "/user/7"]:
    status, val = match_route(rx, int, p)
    print(p, "->", status, val, type(val).__name__ if val is not None else "")

Запусти: /user/abc отсеивается как 404, а число приезжает уже типом int. Точно так конвертеры работают внутри Flask.

Частые ошибки

  • Имя в URL не совпадает с параметром функции. <user_id> и def show(id) — ошибка. Имена должны совпадать буква в букву.
  • Ждать int, а получать string. Без конвертера значение приходит строкой; сравнение с числом даст сюрприз.
  • Использовать path там, где не надо. path ловит слэши и может «съесть» соседние маршруты.

Best practices

  • Ставь конвертер типа — это валидация и защита от мусорных URL бесплатно.
  • Для «человекочитаемых» URL используй slug (string), а не голый id, где это уместно для SEO.
  • Имена переменных делай осмысленными: user_id, а не x.

Что запомнить

  • Переменные части URL пишут в угловых скобках: /user/<int:id>.
  • Конвертер задаёт тип (int, string, path, uuid) и отфильтровывает неподходящие пути.
  • Имя в URL должно совпадать с именем параметра функции.
  • Для SEO предпочитай человекочитаемые slug вместо голых id.

Итог: динамические маршруты — это шаблоны с переменными частями в угловых скобках. Конвертер задаёт тип и заодно отфильтровывает неподходящие пути. Значение приезжает в функцию готовым аргументом нужного типа.

Проверьте себя
1. Что вернёт Flask на /user/abc при маршруте /user/<int:user_id>?
AВызовет функцию с user_id='abc'
B404 — путь не совпал с конвертером int
C500 ошибку
DПреобразует abc в число 0
2. Зачем нужен конвертер path вместо string?
AОн быстрее
Bstring не работает с числами
Cpath захватывает значения со слэшами (например docs/intro.txt)
Dpath шифрует значение