使用 URL 处理器

变更日志

0.7 版新增。

Flask 0.7 引入了 URL 处理器概念。其理念是,你可能拥有一堆在 URL 中具有公共部分的资源,而你并不总是希望明确提供这些部分。例如,你可能拥有一堆包含语言代码的 URL,但你不想在每个单独函数中自行处理它。

当与蓝图结合使用时,URL 处理器特别有用。我们将在本文中处理特定于应用程序的 URL 处理器以及特定于蓝图的 URL 处理器。

国际化应用程序 URL

考虑如下应用程序

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
    ...

这是大量的重复,因为你必须在每个单独函数中自行处理 g 对象上的语言代码设置。当然,可以使用装饰器来简化此操作,但是如果你想从一个函数生成 URL 到另一个函数,你仍然必须明确提供语言代码,这可能会很烦人。

对于后者,这就是 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

URL 映射的 is_endpoint_expecting() 方法可用于确定为给定端点提供语言代码是否有意义。

该函数的逆函数是 url_value_preprocessor()。它们在请求匹配后立即执行,并且可以根据 URL 值执行代码。其理念是,它们从值字典中提取信息并将其放在其他地方

@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
    g.lang_code = values.pop('lang_code', None)

这样,您不必再对每个函数中的 lang_code 分配给 g。您可以通过编写自己的装饰器来进一步改进它,该装饰器使用语言代码作为 URL 前缀,但更漂亮的解决方案是使用蓝图。一旦 '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():
    ...

国际化蓝图 URL

由于蓝图可以自动使用通用字符串作为所有 URL 的前缀,因此可以轻松地为每个函数自动执行此操作。此外,蓝图可以具有按蓝图划分的 URL 处理器,它可以从 url_defaults() 函数中移除大量逻辑,因为它不再需要检查 URL 是否真的对 'lang_code' 参数感兴趣

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():
    ...