Using URL Processors

Changelog

New in version 0.7.

Flask 0.7 introduces the concept of URL processors. The idea is that youmight have a bunch of resources with common parts in the URL that youdon’t always explicitly want to provide. For instance you might have abunch of URLs that have the language code in it but you don’t want to haveto handle it in every single function yourself.

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:

  1. from flask import Flask, g
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.route('/<lang_code>/')
  6. def index(lang_code):
  7. g.lang_code = lang_code
  8. ...
  9.  
  10. @app.route('/<lang_code>/about')
  11. def about(lang_code):
  12. g.lang_code = lang_code
  13. ...

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':

  1. @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:

  1. @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:

  1. from flask import Flask, g
  2.  
  3. app = Flask(__name__)
  4.  
  5. @app.url_defaults
  6. def add_language_code(endpoint, values):
  7. if 'lang_code' in values or not g.lang_code:
  8. return
  9. if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
  10. values['lang_code'] = g.lang_code
  11.  
  12. @app.url_value_preprocessor
  13. def pull_lang_code(endpoint, values):
  14. g.lang_code = values.pop('lang_code', None)
  15.  
  16. @app.route('/<lang_code>/')
  17. def index():
  18. ...
  19.  
  20. @app.route('/<lang_code>/about')
  21. def about():
  22. ...

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:

  1. from flask import Blueprint, g
  2.  
  3. bp = Blueprint('frontend', __name__, url_prefix='/<lang_code>')
  4.  
  5. @bp.url_defaults
  6. def add_language_code(endpoint, values):
  7. values.setdefault('lang_code', g.lang_code)
  8.  
  9. @bp.url_value_preprocessor
  10. def pull_lang_code(endpoint, values):
  11. g.lang_code = values.pop('lang_code')
  12.  
  13. @bp.route('/')
  14. def index():
  15. ...
  16.  
  17. @bp.route('/about')
  18. def about():
  19. ...