Using URL Processors
Changelog
New in version 0.7.
URL processors are especially helpful when combined with blueprints. Wewill handle both application specific URL processors here as well asblueprint 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 repetition as you have to handle the language codesetting on the g
object yourself in every single function.Sure, a decorator could be used to simplify this, but if you want togenerate URLs from one function to another you would have to still providethe language code explicitly which can be annoying.
For the latter, this is where url_defaults()
functionscome in. They can automatically inject values into a call tourl_for()
. The code below checks if thelanguage code is not yet in the dictionary of URL values and if theendpoint wants a value named 'lang_code'
:
- @app.url_defaultsdef 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 is_endpoint_expecting()
of the URLmap can be used to figure out if it would make sense to provide a languagecode for the given endpoint.
The reverse of that function areurl_value_preprocessor()
s. They are executed rightafter the request was matched and can execute code based on the URLvalues. The idea is that they pull information out of the valuesdictionary and put it somewhere else:
- @app.url_value_preprocessordef pull_lang_code(endpoint, values): g.lang_code = values.pop('lang_code', None)
That way you no longer have to do the lang_code assignment tog
in every function. You can further improve that bywriting your own decorator that prefixes URLs with the language code, butthe more beautiful solution is using a blueprint. Once the'lang_code'
is popped from the values dictionary and it will no longerbe 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 stringit’s easy to automatically do that for every function. Furthermoreblueprints can have per-blueprint URL processors which removes a whole lotof logic from the url_defaults()
function because it nolonger 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():
- ...