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