mirror of https://github.com/mitsuhiko/flask.git
Armin Ronacher
14 years ago
2 changed files with 127 additions and 0 deletions
@ -0,0 +1,126 @@
|
||||
Using URL Processors |
||||
==================== |
||||
|
||||
.. versionadded:: 0.7 |
||||
|
||||
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 processors are especially helpful when combined with blueprints. We |
||||
will handle both application specific URL processors here as well as |
||||
blueprint specifics. |
||||
|
||||
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 |
||||
... |
||||
|
||||
This is an awful lot of reptition 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. |
||||
|
||||
For the latter, this is where :func:`~flask.Flask.url_defaults` functions |
||||
come in. They can automatically inject values into a call for |
||||
:func:`~flask.url_for` automatically. 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 |
||||
|
||||
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. |
||||
|
||||
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) |
||||
|
||||
That way you no longer have to do the `lang_code` assigment 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(): |
||||
... |
||||
|
||||
Internationalized Blueprint URLs |
||||
-------------------------------- |
||||
|
||||
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(): |
||||
... |
Loading…
Reference in new issue