1Using URL Processors
2====================
3
4.. versionadded:: 0.7
5
6Flask 0.7 introduces the concept of URL processors.  The idea is that you
7might have a bunch of resources with common parts in the URL that you
8don't always explicitly want to provide.  For instance you might have a
9bunch of URLs that have the language code in it but you don't want to have
10to handle it in every single function yourself.
11
12URL processors are especially helpful when combined with blueprints.  We
13will handle both application specific URL processors here as well as
14blueprint specifics.
15
16Internationalized Application URLs
17----------------------------------
18
19Consider an application like this::
20
21    from flask import Flask, g
22
23    app = Flask(__name__)
24
25    @app.route('/<lang_code>/')
26    def index(lang_code):
27        g.lang_code = lang_code
28        ...
29
30    @app.route('/<lang_code>/about')
31    def about(lang_code):
32        g.lang_code = lang_code
33        ...
34
35This is an awful lot of repetition as you have to handle the language code
36setting on the :data:`~flask.g` object yourself in every single function.
37Sure, a decorator could be used to simplify this, but if you want to
38generate URLs from one function to another you would have to still provide
39the language code explicitly which can be annoying.
40
41For the latter, this is where :func:`~flask.Flask.url_defaults` functions
42come in.  They can automatically inject values into a call to
43:func:`~flask.url_for`.  The code below checks if the
44language code is not yet in the dictionary of URL values and if the
45endpoint wants a value named ``'lang_code'``::
46
47    @app.url_defaults
48    def add_language_code(endpoint, values):
49        if 'lang_code' in values or not g.lang_code:
50            return
51        if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
52            values['lang_code'] = g.lang_code
53
54The method :meth:`~werkzeug.routing.Map.is_endpoint_expecting` of the URL
55map can be used to figure out if it would make sense to provide a language
56code for the given endpoint.
57
58The reverse of that function are
59:meth:`~flask.Flask.url_value_preprocessor`\s.  They are executed right
60after the request was matched and can execute code based on the URL
61values.  The idea is that they pull information out of the values
62dictionary and put it somewhere else::
63
64    @app.url_value_preprocessor
65    def pull_lang_code(endpoint, values):
66        g.lang_code = values.pop('lang_code', None)
67
68That way you no longer have to do the `lang_code` assignment to
69:data:`~flask.g` in every function.  You can further improve that by
70writing your own decorator that prefixes URLs with the language code, but
71the more beautiful solution is using a blueprint.  Once the
72``'lang_code'`` is popped from the values dictionary and it will no longer
73be forwarded to the view function reducing the code to this::
74
75    from flask import Flask, g
76
77    app = Flask(__name__)
78
79    @app.url_defaults
80    def add_language_code(endpoint, values):
81        if 'lang_code' in values or not g.lang_code:
82            return
83        if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
84            values['lang_code'] = g.lang_code
85
86    @app.url_value_preprocessor
87    def pull_lang_code(endpoint, values):
88        g.lang_code = values.pop('lang_code', None)
89
90    @app.route('/<lang_code>/')
91    def index():
92        ...
93
94    @app.route('/<lang_code>/about')
95    def about():
96        ...
97
98Internationalized Blueprint URLs
99--------------------------------
100
101Because blueprints can automatically prefix all URLs with a common string
102it's easy to automatically do that for every function.  Furthermore
103blueprints can have per-blueprint URL processors which removes a whole lot
104of logic from the :meth:`~flask.Flask.url_defaults` function because it no
105longer has to check if the URL is really interested in a ``'lang_code'``
106parameter::
107
108    from flask import Blueprint, g
109
110    bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>')
111
112    @bp.url_defaults
113    def add_language_code(endpoint, values):
114        values.setdefault('lang_code', g.lang_code)
115
116    @bp.url_value_preprocessor
117    def pull_lang_code(endpoint, values):
118        g.lang_code = values.pop('lang_code')
119
120    @bp.route('/')
121    def index():
122        ...
123
124    @bp.route('/about')
125    def about():
126        ...
127