Ответы, редиректы, url_for и коды состояния
View не обязана возвращать только строку. Она управляет статус-кодом, заголовками и редиректами — то есть всем HTTP-ответом целиком.
Ответ — это статус (200, 404, 302), заголовки и тело. Flask даёт удобные инструменты: redirect для перенаправлений, url_for для построения ссылок по имени endpoint, make_response для тонкой настройки.
Статус-код — это короткий «диагноз» ответа. 2xx — успех, 3xx — перенаправление, 4xx — ошибка клиента, 5xx — ошибка сервера. Возвращать правильный код важно: на 404 поисковик уберёт страницу из индекса, на 302 браузер пойдёт по новому адресу.
from flask import redirect, url_for, make_response, abort
@app.route("/old")
def old():
return redirect(url_for("new")) # 302 на endpoint new
@app.route("/new")
def new():
return "Новая страница"
@app.route("/secret")
def secret():
abort(403) # прервать с кодом 403
Ключевой инструмент — url_for. Он строит URL по имени view-функции, а не по строке. Поменяешь путь в маршруте — все ссылки через url_for обновятся сами. Хардкод строк "/new" — путь к битым ссылкам после рефакторинга.
url_for("show_user", user_id=42) # → "/user/42"
url_for("static", filename="app.css") # → "/static/app.css"
url_for стоит сделать рефлексом: каждый раз, когда хочется написать строку URL руками, останавливайся и зови url_for. Выгода видна не сразу, а в момент рефакторинга — когда ты меняешь путь маршрута, и все ссылки в коде и шаблонах обновляются сами, без поиска по проекту и битых страниц. То же касается статики: url_for('static', filename=...) переживёт смену настроек статической папки или подключение CDN. Правильные статус-коды — отдельная дисциплина: 404 убирает несуществующую страницу из индекса, 301 против 302 определяет, закэширует ли браузер переезд, а 4xx против 5xx подсказывает клиенту, чья это ошибка. Возвращать осмысленный код так же важно, как и тело ответа.
Как работает под капотом
url_for делает обратную операцию к маршрутизации: из имени endpoint и параметров собирает путь, подставляя переменные части. Маршрутизация шла «путь → функция», а url_for — «функция → путь». Поэтому ссылки переживают изменение URL.
Маршрут: "/user/<int:user_id>" ──эндпоинт──▶ show_user
▲
url_for("show_user", user_id=42) ──обратно──┘
подставить 42
▼
"/user/42"
Смоделируем url_for: по имени endpoint и шаблону подставим параметры в путь.
import re
url_map = {
"show_user": "/user/<user_id>",
"show_post": "/post/<slug>",
"index": "/",
}
def url_for(endpoint, **params):
template = url_map[endpoint]
def repl(m):
name = m.group(1)
return str(params[name])
return re.sub(r"<(\w+)>", repl, template)
print(url_for("show_user", user_id=42))
print(url_for("show_post", slug="hello-flask"))
print(url_for("index"))
Запусти: имя endpoint + параметры превращаются в готовый путь. Поменяй шаблон в url_map — и все ссылки обновятся, не трогая код вызова. Ровно ради этого Flask настаивает на url_for вместо строк.
Частые ошибки
- Хардкодить URL строками. После смены маршрута ссылки ломаются. Всегда url_for.
- Возвращать redirect без return. redirect только создаёт ответ — его надо вернуть из функции.
- Путать 301 и 302. 301 — навсегда (кэшируется браузером), 302 — временно. Для постоянных переездов бери 301 осознанно.
Best practices
- Все внутренние ссылки — через url_for, в том числе в шаблонах.
- Используй abort(code) для ранних выходов с ошибкой вместо ручной сборки ответа.
- Для кастомных заголовков/кук бери make_response и настраивай объект Response.
Что запомнить
- View управляет всем ответом: телом, статусом, заголовками.
- redirect перенаправляет, abort прерывает запрос с нужным кодом.
- url_for строит устойчивые ссылки по имени endpoint, переживая смену URL.
- Корректный статус-код важен для браузеров и поисковиков.
Итог: view управляет всем ответом — статусом, заголовками, телом. redirect перенаправляет, url_for строит устойчивые ссылки по имени endpoint, abort прерывает с нужным кодом. Это завершает блок про маршруты и запросы — дальше шаблоны.