URL処理機能の利用(Using URL Processors) Using URL Processors

Changelog

バージョン 0.7 で追加.

Flask 0.7は、URL処理機能の概念を導入しました。そのアイデアは、常に明示的に提供したいとは思わないURLの共通部分のあるリソースが大量にあるかもしれない、というものです。例えば、URLの中に言語コードを持つ大量のURLがあり、全ての一つ一つの関数でそれ(言語コード)を自分で処理したいとは思わないかもしれません。 Flask 0.7 introduces the concept of URL processors. The idea is that you might have a bunch of resources with common parts in the URL that you don't always explicitly want to provide. For instance you might have a bunch of URLs that have the language code in it but you don't want to have to handle it in every single function yourself.

URL処理機能は、blueprintと組み合わせると特に便利です。ここでは、Flaskアプリケーションが指定するURLだけでなく、blueprintが指定するものも扱います。 URL processors are especially helpful when combined with blueprints. We will handle both application specific URL processors here as well as blueprint specifics.

国際化されたアプリケーションのURL(Internationalized Application URLs) Internationalized Application URLs

以下のようなアプリケーションを考えます: Consider an application like this::

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を生成したいときは、まだ言語コードを明示的に提供する必要があるかもしれず、それは気が滅入るものでしょう。 This is an awful lot of repetition as you have to handle the language code setting on the :data:`~flask.g` object yourself in every single function. Sure, a decorator could be used to simplify this, but if you want to generate URLs from one function to another you would have to still provide the language code explicitly which can be annoying.

後者については、これはurl_defaults()関数が入って来るところです。それらは、url_for()呼び出しの中へ自動的に値を挿入します。以下のコードは、URLの値のdictionaryの中に言語コードがまだないかを調べ、さらにエンドポイントが'lang_code'という名前の値を求めるか調べます: For the latter, this is where :func:`~flask.Flask.url_defaults` functions come in. They can automatically inject values into a call to :func:`~flask.url_for`. The code below checks if the language code is not yet in the dictionary of URL values and if the endpoint wants a value named ``'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_mapのis_endpoint_expecting()メソッドは、与えられたエンドポイントに対して言語コードを提供することが理にかなっているかどうかを理解するために使用できます。 The method :meth:`~werkzeug.routing.Map.is_endpoint_expecting` of the URL map can be used to figure out if it would make sense to provide a language code for the given endpoint.

その関数の反対がurl_value_preprocessor()です。それらはリクエストが(URLのruleに)合致した直後に実行され、URLの値に基づいてコードを実行できます。そのアイデアは、それらは(URLの)値のdictionaryから情報を引き出し、それをどこか別のところに押し入れます: The reverse of that function are :meth:`~flask.Flask.url_value_preprocessor`\s. They are executed right after the request was matched and can execute code based on the URL values. The idea is that they pull information out of the values dictionary and put it somewhere else::

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

こうすると、lang_codegへの割り当てを全ての関数で行う必要が、もはやなくなります。言語コードをURLの接頭辞にする、自分独自のデコレータを書いてさらに改善できますが、より美しい解決策はblueprintを使うことです。一度'lang_code'が値のdictionaryから取り出されたら、それはもはやview関数へ転送さることはなくなり、コードを以下のように小さくします: That way you no longer have to do the `lang_code` assignment to :data:`~flask.g` in every function. You can further improve that by writing your own decorator that prefixes URLs with the language code, but the more beautiful solution is using a blueprint. Once the ``'lang_code'`` is popped from the values dictionary and it will no longer be forwarded to the view function reducing the code to this::

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のURL(Internationalized Blueprint URLs) Internationalized Blueprint URLs

blueprintは自動的に共通の文字列をすべてのURLの先頭に付けるため、全ての関数でそれ(上述のlang_code処理の様な共通処理)を自動的に行うことは容易です。さらにblueprintは、URLが'lang_code'パラメータに本当に関心があるのかもはや調べる必要がなくなるため、url_defaults()関数から一切合切のロジックを取り除くURL処理機能を、blueprint毎に持つことができます: Because blueprints can automatically prefix all URLs with a common string it's easy to automatically do that for every function. Furthermore blueprints can have per-blueprint URL processors which removes a whole lot of logic from the :meth:`~flask.Flask.url_defaults` function because it no longer has to check if the URL is really interested in a ``'lang_code'`` parameter::

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