Следующая важная
и очень распространенная функция, с которой мы познакомимся – это url_for(). Она
позволяет генерировать URL-адрес по имени функции-обработчика.
Например, рассмотрим вот такую программу:
from flask import Flask, render_template, url_for
app = Flask(__name__)
menu = ["Установка", "Первое приложение", "Обратная связь"]
@app.route("/")
def index():
print( url_for('index') )
return render_template('index.html', menu = menu)
@app.route("/about")
def about():
print( url_for('about') )
return render_template('about.html', title = "О сайте", menu = menu)
if __name__ == "__main__":
app.run(debug=True)
Мы здесь
дополнительно импортировали функцию url_for, а затем,
вызываем ее в обработчиках index и about. Если теперь
посетить данные страницы, то в консоли увидим соответствующие URL:
/ и /about
Но спрашивается:
зачем она нам нужна, мы же и так знаем эти URL, поэтому, что
нам мешает их явно указать? Если мы так будем делать, то столкнемся с
множеством проблем. Во-первых, URL могут измениться, и тогда нам придется
везде их менять, а если где-то забудем, то это негативно скажется на
отображении страниц сайта. Или же, URL могут генерироваться динамически
и тогда мы в принципе не сможем их явно прописать. Наконец, один и тот же
шаблон для разных приложений, возможно, должен подставлять разный URL, например, для
подключения внешних CSS или JS файлов. И это лишь малая толика
проблем, с которыми мы можем столкнуться. Поэтому использовать явную запись URL – это пагубная
практика и от нее лучше сразу отказаться, используя взамен функцию url_for. Ее полный
синтаксис, следующий:
url_for(endpoint, **values)
где values – словарь
именованных аргументов.
Вызов данной
функции непосредственно связан с контекстом запроса и вне его она просто не
будет работать. То есть, если выполнить ее за пределами наших обработчиков,
например, так:
app = Flask(__name__)
menu = ["Установка", "Первое приложение", "Обратная связь"]
@app.route("/")
def index():
return render_template('index.html', menu = menu)
@app.route("/about")
def about():
return render_template('about.html', title = "О сайте", menu = menu)
print( url_for('index') )
То возникнет
ошибка отсутствия контекста вызова. Как же можно в целях отладки тестировать ее
работу? Для этого фреймворк Flask позволяет искусственно создавать
контекст запроса без активации веб-сервера. Это делается так:
with app.test_request_context():
print( url_for('index') )
Теперь все
сработает и в консоли мы увидим URL для index.
Также эта
функция корректно работает и в случае использования нескольких WSGI-приложений. Она
обращается к переменной current_app и затем, берет URL из активного
приложения.
Способы описания URL
В предыдущих
примерах мы видели как с помощью декоратора route происходит
привязка функции к URL-адресу. Но эти адреса можно делать и переменными,
используя следующий синтаксис:
@app.route("/url/<variable>")
Здесь variable – это некоторая
переменная, значение которой определяется URL-адресом.
Например, можно сделать так:
@app.route("/profile/<username>")
def profile(username):
return f"Пользователь: {username}"
И если в
браузере набрать запрос:
http://127.0.0.1:5000/profile/selfedu
то username примет значение selfedu. Или можно создать
такой запрос:
http://127.0.0.1:5000/profile/12345678
Соответственно, username = «12345678»,
то есть все, что указывается в URL, записывается в виде строки.
При необходимости
мы можем добавлять конверторы при определении переменных, например, указать,
что следует использовать только целые числа:
@app.route("/profile/<int:username>")
Тогда при
запросе вида:
http://127.0.0.1:5000/profile/12345678fff
Сервер вернет
код страницы 404 – не существующий URL. И все потому, что
предполагается запись в поле username из цифр. Если убрать буквы и
обновить страницу, то все сработает.
В качестве
конверторов можно использовать следующие обозначения:
-
int – должны присутствовать
только цифры;
-
float – можно
записывать число с плавающей точкой;
-
path – можно
использовать любые допустимые символы URL плюс символ слеша
‘/’.
Например, при
конверторе path:
@app.route("/profile/<path:username>")
Можно
сформировать следующий запрос:
http://127.0.0.1:5000/profile/12345678ddd/fdfgh
и переменная username = «12345678ddd/fdfgh». Но, если
убрать это определение:
@app.route("/profile/<username>")
то этот же
запрос приведет к ошибке 404, т.к. обратный слеш воспринимается как продолжение
URL, не относящееся
к полю username.
В заключение
этого занятия давайте посмотрим на работу функции url_for для переменных URL. Для создадим
тестовый контекст запроса и в нем вызовем url_for для разных
представлений:
with app.test_request_context():
print(url_for('index'))
print(url_for('about'))
print(url_for('profile', username="selfedu"))
В консоли увидим
следующие строчки:
/
/about
/profile/selfedu
Обратите
внимание на последний вызов. Так как для profile реализован
переменный URL с полем username, то этот
параметр нужно явно прописать, чтобы получить конечный вид URL-адреса. Без
этого параметра функция url_for приведет к
ошибке.
Итак, на этом
занятии мы с вами рассмотрели понятия контекста приложения и контекста запроса,
познакомились с функцией url_for и увидели как
можно описывать переменные URL-адреса.