Сообщить об ошибке.

Правила составления URL-маршрутов во Flask Python

Составление GET/POST маршрутов, редиректы, субдомены во Flask

Содержание:


Правила составления маршрутов (конверторы) во Flask.

Фреймворк Flask предоставляет три способа определить правила для системы URL маршрутизации сайта:

  • Можно использовать декоратор экземпляра приложения или blueprint: @flask.Flask.route().
  • Можно использовать метод экземпляра приложения или blueprint: flask.Flask.add_url_rule().
  • Можно напрямую получить доступ к базовой системе маршрутизации библиотеки Werkzeug, которая представлена ​​как flask.Flask.url_map().

Переменные части маршрута указываются в угловых скобках /<user>/<username>. По умолчанию переменная часть в URL-адресе принимает любую строку без косой черты /. Можно указать другой конвертер, используя схему: <converter:name>, где converter - это один из конвертеров (указанных ниже), а - name - имя переменной части маршрута.

Переменные части маршрута name передаются в функцию-представление, как ключевые аргументы, которая в свою очередь возвращает ответ сервера.

Во Flask доступны следующие конвертеры (преобразователи):

  • string: принимает любой текст без косой черты (по умолчанию);
  • int: принимает целые числа;
  • float: как int, но для значений с плавающей запятой;
  • path: принимает любой текст, но также принимает косые черты;
  • any: соответствует одному из предоставленных элементов;
  • uuid: принимает строки UUID.

Примеры маршрутизации с использованием декоратора @app.route():

@app.route('/')
def index():
    pass

@app.route('/<username>')
def show_user(username):
    pass

@app.route('/post/<int:post_id>', methods=['GET', 'POST')
def show_post(post_id):
    pass

Примеры маршрутизации, с использованием метода app.add_url_rule():

Метод app.add_url_rule() регистрирует правило для маршрутизации входящих запросов и построения URL-адресов. Декоратор @app.route() - это ссылка для вызова метода app.add_url_rule() с аргументом view_func.

def index():
    pass

def show_user(username):
    pass

def show_post(post_id):
    pass

app.add_url_rule('/', view_func=index)
app.add_url_rule('/<username>', view_func=show_user, methods=['GET', 'POST')
app.add_url_rule('/post/<int:post_id>', view_func=show_post)

Важная деталь, о которой следует помнить, - это то, как Flask обрабатывает завершающие косые черты. Идея состоит в том, чтобы каждый URL оставался уникальным:

  • Если маршрут заканчивается косой чертой, а он запрашивается пользователем без косой черты, то пользователь автоматически перенаправляется на ту же страницу с добавленной косой чертой.
  • Если правило не заканчивается косой чертой в конц, а и пользователь запрашивает страницу с косой чертой в конце, то возникает ошибка 404 not found.

Это соответствует тому, как веб-серверы работают со статическими файлами. Это также позволяет безопасно использовать относительные цели ссылок.

Значения по умолчанию для маршрутов во Flask.

Можно определить несколько URL-маршрутов для одной и той же функции-представления, при этом маршруты должны быть уникальными. Также можно указать значения по умолчанию для переменных значений маршрутов.

Пример:

@app.route('/users/', defaults={'page': 1})
@app.route('/users/page/<int:page>')
def show_users(page):
    pass

Код указывает, что маршрут /users/ будет URL-адресом для первой страницы, а /users/page/N будет URL-адресом для страницы N.

Если URL-адрес содержит значение по умолчанию, то он будет перенаправлен в более простую форму с редиректом 301. В приведенном выше примере /users/page/1 будет перенаправлен на URL-адрес /users/. Если маршрут обрабатывает запросы GET и POST, убедитесь, что маршрут по умолчанию обрабатывает только GET запросы, так как при редиректах невозможно сохранить данные формы.

@app.route('/region/', defaults={'id': 1})
@app.route('/region/<int:id>', methods=['GET', 'POST'])
def region(id):
   pass

Использование HTTP-методов (GET, POST и т. д.) при обращении к URL-адресу.

Веб-приложения могут использовать разные HTTP-методы при доступе к URL-адресу. По умолчанию маршрут URL-адреса во Flask отвечает только на GET-запросы (их можно не указывать). Для обработки URL-адреса другим методом или несколькими HTTP-методами необходимо использовать аргумент methods в декораторе экземпляра веб-приложения @app.route() или метода app.add_url_rule().

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # обработать форму
        do_the_login()
    else:
        # отобразить форму
        show_the_login_form()

Если присутствует HTTP-метод GET, то Flask автоматически добавляет поддержку HTTP-метода HEAD и обрабатывает запросы HEAD в соответствии с HTTP RFC. Аналогичным образом, автоматически реализуется HTTP-метод OPTIONS, при наличии POST.

С версии Flask 2.x, доступны декораторы-помощники, в которых одноименный метод обращения к маршруту уже подставлен:

  • @app.delete() - ссылка для app.route() с подставленным аргументом methods=['DELETE'];
  • @app.get() - ссылка для app.route() с подставленным аргументом methods=['GET'];
  • @app.patch() - ссылка для app.route() с подставленным аргументом methods=['PATCH'];
  • @app.post() - ссылка для app.route() с подставленным аргументом methods=['POST'];
  • @app.put() - ссылка для app.route() с подставленным аргументом methods=['PUT'];

Пример использования:

@app.post('/login')
def index():
    pass

# эквивалентен
@app.route('/login', methods=['POST'])
def index():
    pass

Конвертер маршрута по регулярному выражению.

Для создания собственного конвертора маршрута необходимо наследоваться от класса базового конвертора werkzeug.routing.BaseConverter, а для его регистрации или изменения уже имеющихся конвертеров, можно использовать метод экземпляра приложения app.url_map()

Изменение или регистрация нового конвертера маршрутизации осуществляется после создания класса конвертера, но до подключения каких-либо маршрутов.

Конвертер на основе регулярных выражений должен иметь атрибут regex с соответствующим регулярным выражением. Если конвертер может принимать аргументы в правиле URL-адреса, он должен принимать их в своем методе __init__.

Пример конвертера на основе регулярных выражений:

from flask import Flask
from werkzeug.routing import BaseConverter

# создаем экземпляр веб-приложения
app = Flask(__name__)

# определяем класс конвертера регулярных выражений
class RegexConverter(BaseConverter):
    def __init__(self, url_map, *items):
        super(RegexConverter, self).__init__(url_map)
        self.regex = items[0]

# регистрируем конвертер
# правила использования: `<regex():name>`
app.url_map.converters['regex'] = RegexConverter

# определяем маршрут на основе регулярного выражения
@app.route('/<regex('[abcABC0-9]{4,6}'):uid>-<slug>/')
def example(uid, slug):
    return f'uid: {uid}, slug: {slug}'

Теперь URL-адрес http://localhost:5000/abc9-foo/ возвратит код ответа 200, а функция-представление example() - строку 'uid: abc9, slug: foo'. URL-адрес http://localhost:5000/abcd-foo/ будет неправильным и станет генерировать ошибку 404:

Пользовательские конвертеры, соответствующие произвольным выражениям.

Класс пользовательского конвертера может реализовать метод .to_python() для преобразования согласованной строки в какой-либо другой объект, что также может выполнить дополнительную проверку, которая была невозможна с атрибутом regex, и в этом случае должна вызвать ошибку werkzeug.routing.ValidationError. Возникновение любых других ошибок приведет к ошибке 500.

Конвертер может реализовать метод .to_url() для преобразования объекта Python в строку при построении URL-адреса. Любая возникшая здесь ошибка будет преобразована в ошибку werkzeug.routing.BuildError и в конечном итоге вызовет ошибку 500.

В этом примере реализуется BooleanConverter, который будет соответствовать строкам , 'yes', 'no', и 'maybe', возвращая случайное значение False или True для URL с 'maybe'.

from flask import Flask
from werkzeug.routing import BaseConverter, ValidationError
from random import randrange

# создаем экземпляр веб-приложения
app = Flask(__name__)

# определяем класс конвертера
class BooleanConverter(BaseConverter):
    regex = r"(?:yes|no|maybe)"

    def __init__(self, url_map, maybe=False):
        super().__init__(url_map)
        self.maybe = maybe

    def to_python(self, value):
        # если прилетает `maybe`
        if value == "maybe":
            if self.maybe:
                # генерируем случайный `False` или `True`
                return bool(randrange(2))
            raise ValidationError
        # если `value` будет равно 'yes', то возвращается 
        # `True`, если 'no' - то `False`
        return value == 'yes'

# регистрируем конвертер
# правила использования: `<bool():name>`
app.url_map.converters['bool'] = BooleanConverter

# маршрут только для `/yes` или `/no`
## Поддержка субдоиенов в URL-маршрутах.('/<bool:rocks>')
def vote(rocks):
    return f'vote: {rocks}'

# выражение `bool(maybe=True)` к маршрутам `/talk/yes` 
# и `/talk/no` дополнительно включает поддержку `/talk/maybe`
@app.route('/talk/<bool(maybe=True):foo>')
def talk(foo):
    return f'talk: {foo}'

if __name__ == '__main__':
    app.run(debug=True, port=5000)

Поддержка субдоменов в URL-маршрутах Flask.

Декоратор экземпляра приложения @app.route() и его метод app.add_url_rule() принимает аргумент поддомена subdomain, сопоставления маршрута с поддоменом. Экземпляр схемы blueprint также принимает этот аргумент, чтобы установить соответствие поддомена для всех маршрутов в определенной схеме blueprint.

Для успешного обслуживания доменов необходимо установить базовый домен в конфигурации веб-приложения app.config['SERVER_NAME'], чтобы Flask знал, с чем сопоставлять. Также нужно будет указать порт, если только приложение не работает на порту 80 или 443 (т. е. в рабочей среде).

Начиная с Flask 1.0, также необходимо установить аргумент subdomain_matching=True при создании объекта приложения.

from flask import Flask

app = Flask(__name__, subdomain_matching=True)
app.config['SERVER_NAME'] = "example.com:5000"

@app.route("/")
def index():
    return "example.com"

@app.route("/", subdomain="<subdomain>")
def sub_index(subdomain):
    return f'{subdomain}.example.com'

Пример для приложения со схемами Blueprint:

egg = Blueprint("egg", __name__, subdomain="egg")

@egg.route("/")
def index():
    return "egg.example.com"

При локальном запуске необходимо отредактировать файл hosts на компьютере (/etc/hosts в Unix), чтобы он знал, как маршрутизировать поддомены, т. к. домены локально не существуют.

# добавьте строку в файл `hosts`
127.0.0.1 localhost example.com egg.example.com

Не забудьте указать порт в браузере: http://example.com:5000, http://egg.example.com:5000 и т. д.

При развертывании приложения на боевом сервере, необходимо настроить DNS и прокси-сервер (например Nginx) для маршрутизации всех поддоменов веб-приложения.

Помните, что все маршруты Flask на самом деле являются экземплярами werkzeug.routing.Rule. Обращение к документации Werkzeug для Rule покажет вам довольно много вещей, которые могут делать маршруты, которые не учитываются в документации Flask, т.к. они хорошо документированы в werkzeug.