Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/studio/static/doc/flask-docs/_sources/patterns/urlprocessors.txt
blob: 778a5a6b1b055e75ca61328f0301a6ce82fb0aec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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 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.

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