| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | View Decorators
 | 
					
						
							|  |  |  | ===============
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Python has a really interesting feature called function decorators.  This
 | 
					
						
							| 
									
										
										
										
											2014-03-27 00:52:06 +08:00
										 |  |  | allows some really neat things for web applications.  Because each view in
 | 
					
						
							|  |  |  | Flask is a function, decorators can be used to inject additional
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | functionality to one or more functions.  The :meth:`~flask.Flask.route`
 | 
					
						
							|  |  |  | decorator is the one you probably used already.  But there are use cases
 | 
					
						
							|  |  |  | for implementing your own decorator.  For instance, imagine you have a
 | 
					
						
							| 
									
										
										
										
											2014-03-27 00:52:06 +08:00
										 |  |  | view that should only be used by people that are logged in.  If a user
 | 
					
						
							| 
									
										
										
										
											2011-06-28 20:34:07 +08:00
										 |  |  | goes to the site and is not logged in, they should be redirected to the
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | login page.  This is a good example of a use case where a decorator is an
 | 
					
						
							|  |  |  | excellent solution.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Login Required Decorator
 | 
					
						
							|  |  |  | ------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | So let's implement such a decorator.  A decorator is a function that
 | 
					
						
							| 
									
										
										
										
											2016-06-06 05:21:17 +08:00
										 |  |  | wraps and replaces another function.  Since the original function is
 | 
					
						
							|  |  |  | replaced, you need to remember to copy the original function's information
 | 
					
						
							|  |  |  | to the new function.  Use :func:`functools.wraps` to handle this for you.
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | This example assumes that the login page is called ``'login'`` and that
 | 
					
						
							| 
									
										
										
										
											2016-06-06 05:21:17 +08:00
										 |  |  | the current user is stored in ``g.user`` and is ``None`` if there is no-one
 | 
					
						
							|  |  |  | logged in. ::
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     from functools import wraps
 | 
					
						
							|  |  |  |     from flask import g, request, redirect, url_for
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def login_required(f):
 | 
					
						
							|  |  |  |         @wraps(f)
 | 
					
						
							|  |  |  |         def decorated_function(*args, **kwargs):
 | 
					
						
							|  |  |  |             if g.user is None:
 | 
					
						
							|  |  |  |                 return redirect(url_for('login', next=request.url))
 | 
					
						
							|  |  |  |             return f(*args, **kwargs)
 | 
					
						
							|  |  |  |         return decorated_function
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-06 05:21:17 +08:00
										 |  |  | To use the decorator, apply it as innermost decorator to a view function.
 | 
					
						
							|  |  |  | When applying further decorators, always remember
 | 
					
						
							|  |  |  | that the :meth:`~flask.Flask.route` decorator is the outermost. ::
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @app.route('/secret_page')
 | 
					
						
							|  |  |  |     @login_required
 | 
					
						
							|  |  |  |     def secret_page():
 | 
					
						
							|  |  |  |         pass
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-06 05:21:17 +08:00
										 |  |  | .. note::
 | 
					
						
							|  |  |  |     The ``next`` value will exist in ``request.args`` after a ``GET`` request for
 | 
					
						
							|  |  |  |     the login page.  You'll have to pass it along when sending the ``POST`` request
 | 
					
						
							|  |  |  |     from the login form.  You can do this with a hidden input tag, then retrieve it
 | 
					
						
							|  |  |  |     from ``request.form`` when logging the user in. ::
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         <input type="hidden" value="{{ request.args.get('next', '') }}"/>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | Caching Decorator
 | 
					
						
							|  |  |  | -----------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Imagine you have a view function that does an expensive calculation and
 | 
					
						
							|  |  |  | because of that you would like to cache the generated results for a
 | 
					
						
							|  |  |  | certain amount of time.  A decorator would be nice for that.  We're
 | 
					
						
							| 
									
										
										
										
											2020-04-05 03:57:14 +08:00
										 |  |  | assuming you have set up a cache like mentioned in :doc:`caching`.
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-27 00:52:06 +08:00
										 |  |  | Here is an example cache function.  It generates the cache key from a
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  | specific prefix (actually a format string) and the current path of the
 | 
					
						
							|  |  |  | request.  Notice that we are using a function that first creates the
 | 
					
						
							|  |  |  | decorator that then decorates the function.  Sounds awful? Unfortunately
 | 
					
						
							|  |  |  | it is a little bit more complex, but the code should still be
 | 
					
						
							|  |  |  | straightforward to read.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The decorated function will then work as follows
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-26 02:33:41 +08:00
										 |  |  | 1. get the unique cache key for the current request based on the current
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  |    path.
 | 
					
						
							|  |  |  | 2. get the value for that key from the cache. If the cache returned
 | 
					
						
							|  |  |  |    something we will return that value.
 | 
					
						
							|  |  |  | 3. otherwise the original function is called and the return value is
 | 
					
						
							|  |  |  |    stored in the cache for the timeout provided (by default 5 minutes).
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Here the code::
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     from functools import wraps
 | 
					
						
							|  |  |  |     from flask import request
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-26 14:19:00 +08:00
										 |  |  |     def cached(timeout=5 * 60, key='view/{}'):
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  |         def decorator(f):
 | 
					
						
							|  |  |  |             @wraps(f)
 | 
					
						
							|  |  |  |             def decorated_function(*args, **kwargs):
 | 
					
						
							| 
									
										
										
										
											2019-10-26 14:19:00 +08:00
										 |  |  |                 cache_key = key.format(request.path)
 | 
					
						
							| 
									
										
										
										
											2010-04-26 00:55:01 +08:00
										 |  |  |                 rv = cache.get(cache_key)
 | 
					
						
							|  |  |  |                 if rv is not None:
 | 
					
						
							|  |  |  |                     return rv
 | 
					
						
							|  |  |  |                 rv = f(*args, **kwargs)
 | 
					
						
							|  |  |  |                 cache.set(cache_key, rv, timeout=timeout)
 | 
					
						
							|  |  |  |                 return rv
 | 
					
						
							|  |  |  |             return decorated_function
 | 
					
						
							|  |  |  |         return decorator
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-05 03:57:14 +08:00
										 |  |  | Notice that this assumes an instantiated ``cache`` object is available, see
 | 
					
						
							|  |  |  | :doc:`caching`.
 | 
					
						
							| 
									
										
										
										
											2010-05-02 18:08:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Templating Decorator
 | 
					
						
							|  |  |  | --------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | A common pattern invented by the TurboGears guys a while back is a
 | 
					
						
							|  |  |  | templating decorator.  The idea of that decorator is that you return a
 | 
					
						
							|  |  |  | dictionary with the values passed to the template from the view function
 | 
					
						
							|  |  |  | and the template is automatically rendered.  With that, the following
 | 
					
						
							|  |  |  | three examples do exactly the same::
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route('/')
 | 
					
						
							|  |  |  |     def index():
 | 
					
						
							|  |  |  |         return render_template('index.html', value=42)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route('/')
 | 
					
						
							|  |  |  |     @templated('index.html')
 | 
					
						
							|  |  |  |     def index():
 | 
					
						
							|  |  |  |         return dict(value=42)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route('/')
 | 
					
						
							|  |  |  |     @templated()
 | 
					
						
							|  |  |  |     def index():
 | 
					
						
							|  |  |  |         return dict(value=42)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | As you can see, if no template name is provided it will use the endpoint
 | 
					
						
							| 
									
										
										
										
											2010-05-02 18:11:04 +08:00
										 |  |  | of the URL map with dots converted to slashes + ``'.html'``.  Otherwise
 | 
					
						
							|  |  |  | the provided template name is used.  When the decorated function returns,
 | 
					
						
							|  |  |  | the dictionary returned is passed to the template rendering function.  If
 | 
					
						
							| 
									
										
										
										
											2014-11-05 11:04:58 +08:00
										 |  |  | ``None`` is returned, an empty dictionary is assumed, if something else than
 | 
					
						
							| 
									
										
										
										
											2010-08-14 05:37:57 +08:00
										 |  |  | a dictionary is returned we return it from the function unchanged.  That
 | 
					
						
							|  |  |  | way you can still use the redirect function or return simple strings.
 | 
					
						
							| 
									
										
										
										
											2010-05-02 18:08:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-27 00:52:06 +08:00
										 |  |  | Here is the code for that decorator::
 | 
					
						
							| 
									
										
										
										
											2010-05-02 18:08:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     from functools import wraps
 | 
					
						
							| 
									
										
										
										
											2014-03-27 00:40:25 +08:00
										 |  |  |     from flask import request, render_template
 | 
					
						
							| 
									
										
										
										
											2010-05-02 18:08:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def templated(template=None):
 | 
					
						
							|  |  |  |         def decorator(f):
 | 
					
						
							|  |  |  |             @wraps(f)
 | 
					
						
							|  |  |  |             def decorated_function(*args, **kwargs):
 | 
					
						
							|  |  |  |                 template_name = template
 | 
					
						
							|  |  |  |                 if template_name is None:
 | 
					
						
							| 
									
										
										
										
											2021-05-21 17:46:31 +08:00
										 |  |  |                     template_name = f"{request.endpoint.replace('.', '/')}.html"
 | 
					
						
							| 
									
										
										
										
											2010-05-02 18:08:17 +08:00
										 |  |  |                 ctx = f(*args, **kwargs)
 | 
					
						
							|  |  |  |                 if ctx is None:
 | 
					
						
							|  |  |  |                     ctx = {}
 | 
					
						
							| 
									
										
										
										
											2010-08-14 05:37:57 +08:00
										 |  |  |                 elif not isinstance(ctx, dict):
 | 
					
						
							|  |  |  |                     return ctx
 | 
					
						
							| 
									
										
										
										
											2010-05-02 18:08:17 +08:00
										 |  |  |                 return render_template(template_name, **ctx)
 | 
					
						
							|  |  |  |             return decorated_function
 | 
					
						
							|  |  |  |         return decorator
 | 
					
						
							| 
									
										
										
										
											2011-01-23 20:31:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Endpoint Decorator
 | 
					
						
							|  |  |  | ------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | When you want to use the werkzeug routing system for more flexibility you
 | 
					
						
							| 
									
										
										
										
											2014-11-05 11:04:58 +08:00
										 |  |  | need to map the endpoint as defined in the :class:`~werkzeug.routing.Rule`
 | 
					
						
							|  |  |  | to a view function. This is possible with this decorator. For example::
 | 
					
						
							| 
									
										
										
										
											2011-01-23 20:31:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     from flask import Flask
 | 
					
						
							|  |  |  |     from werkzeug.routing import Rule
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-05 11:04:58 +08:00
										 |  |  |     app = Flask(__name__)
 | 
					
						
							|  |  |  |     app.url_map.add(Rule('/', endpoint='index'))
 | 
					
						
							| 
									
										
										
										
											2011-01-23 20:31:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-05 11:04:58 +08:00
										 |  |  |     @app.endpoint('index')
 | 
					
						
							|  |  |  |     def my_index():
 | 
					
						
							|  |  |  |         return "Hello world"
 |