С версии 0.7, фреймворк Flask представляет концепцию обработчиков URL или как их еще называют: URL-процессоры. Идея состоит в том, чтобы общие части в URL-адреса не обрабатывались каждая в отдельной функции-представлении. Например, есть несколько URL-адресов, содержащих код языка, и необходимо сделать так, что бы исключить обработку языка в каждой отдельной функции.
URL-процессоры особенно полезны в сочетании со схемами blueprint
. В этом материале рассмотрим использование URL-процессоров в конкретных приложениях, так и особенности применения со схемами blueprint
.
Рассмотрим такое приложение:
from flask import Flask, g app = Flask(__name__) @app.route('/<lang_code>/') def index(lang_code): g.lang_code = lang_code ... @app.route('/<lang_code>/about') def about(lang_code): g.lang_code = lang_code ...
Из приведенного кода видно, что язык пользователя обрабатывается в каждой функции-представлении, и это очень много повторений, а следовательно и кода. Для упрощения всего этого можно использовать декоратор, но если генерировать URL-адреса от одной функции к другой, то все равно придется явно указывать языковой код пользователя, что может раздражать.
Для последнего действия, на помощь приходит декоратор экземпляра приложения @app.url_defaults()
, который может автоматически вставлять значения в вызов url_for()
. Приведенный ниже код проверяет, нет ли языкового кода еще в словаре значений URL и требуется ли конечной точке значение с именем lang_code
:
@app.url_defaults def add_language_code(endpoint, values): if 'lang_code' in values or not g.lang_code: return if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): values['lang_code'] = g.lang_code
Здесь, для выяснения, стоит ли передавать языковой код для данной конечной точки, используется метод app.url_map.is_endpoint_expecting()
.
Обратной этому методу, является декоратор @app.url_value_preprocessor()
. Он выполняются сразу после сопоставления запроса и может выполнять код на основе изменяемых значений URL. Идея состоит в том, что он извлекает информацию из словаря значений и помещает ее в другое место:
@app.url_value_preprocessor def pull_lang_code(endpoint, values): g.lang_code = values.pop('lang_code', None)
Таким образом, больше не нужно назначать lang_code
для объекта flask.g
в каждой функции. Можно еще больше улучшить поведение, написав свой собственный декоратор, который добавляет к URL-адресам код языка, но более красивым решением является использование схемы blueprint
. После того, как lang_code
будет извлечен из словаря значений и больше не будет перенаправлен в функцию-представление, код сократиться до следующего:
from flask import Flask, g app = Flask(__name__) @app.url_defaults def add_language_code(endpoint, values): if 'lang_code' in values or not g.lang_code: return if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): values['lang_code'] = g.lang_code @app.url_value_preprocessor def pull_lang_code(endpoint, values): g.lang_code = values.pop('lang_code', None) @app.route('/<lang_code>/') def index(): ... @app.route('/<lang_code>/about') def about(): ...
blueprint
.Так как схемы blueprint
могут автоматически подставлять префикс для всех URL-адресов, это легко сделать автоматически для каждой функции-представления. Кроме того, в blueprint
можно определить URL-процессор для каждой схемы, что уберет большую часть логики из декоратора blueprint.url_defaults()
, т.к. ему больше не надо проверять, нужен ли lang_code
URL-адресу:
from flask import Blueprint, g bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>') @bp.url_defaults def add_language_code(endpoint, values): values.setdefault('lang_code', g.lang_code) @bp.url_value_preprocessor def pull_lang_code(endpoint, values): g.lang_code = values.pop('lang_code') @bp.route('/') def index(): ... @bp.route('/about') def about(): ...