1. Armin Ronacher
  2. flask


Armin Ronacher  committed f05c571

Added docs on caching and decorators.

  • Participants
  • Parent commits 9fd744c
  • Branches default

Comments (0)

Files changed (3)

File docs/patterns/caching.rst

View file
+.. _caching-pattern:
+When your application runs slow, throw some caches in.  Well, at least
+it's the easiest way to speed up things.  What does a cache do?  Say you
+have a function that takes some time to complete but the results would
+still be good enough if they were 5 minutes old.  So then the idea is that
+you actually put the result of that calculation into a cache for some
+Flask itself does not provide caching for you, but Werkzeug, one of the
+libraries it is based on, has some very basic cache support.  It supports
+multiple cache backends, normally you want to use a memcached server.
+Setting up a Cache
+You create a cache object once and keep it around, similar to how
+:class:`~flask.Flask` objects are created.  If you are using the
+development server you can create a
+:class:`~werkzeug.contrib.cache.SimpleCache` object, that one is a simple
+cache that keeps the item stored in the memory of the Python interpreter::
+    from werkzeug.contrib.cache import SimpleCache
+    cache = SimpleCache()
+If you want to use memcached, make sure to have one of the memcache modules
+supported (you get them from `PyPI <http://pypi.python.org/>`_) and a
+memcached server running somewhere.  This is how you connect to such an
+memcached server then::
+    from werkzeug.contrib.cache import MemcachedCache
+    cache = MemcachedCache([''])
+If you are using appengine, you can connect to the appengine memcache
+server easily::
+    from werkzeug.contrib.cache import GAEMemcachedCache
+    cache = GAEMemcachedCache()
+Using a Cache
+Now how can one use such a cache?  There are two very important
+operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and 
+:meth:`~werkzeug.contrib.cache.BaseCache.set`.  This is how to use them:
+To get an item from the cache call
+:meth:`~werkzeug.contrib.cache.BaseCache.get` with a string as key name.
+If something is in the cache, it is returned.  Otherwise that function
+will return `None`::
+    rv = cache.get('my-item')
+To add items to the cache, use the :meth:`~werkzeug.contrib.cache.BaseCache.set`
+method instead.  The first argument is the key and the second the value
+that should be set.  Also a timeout can be provided after which the cache
+will automatically remove item.
+Here a full example how this looks like normally::
+    def get_my_item():
+        rv = cache.get('my-item')
+        if rv is None:
+            rv = calculate_value()
+            cache.set('my-item', rv, timeout=5 * 60)
+        return rv

File docs/patterns/index.rst

View file
+   caching
+   viewdecorators

File docs/patterns/viewdecorators.rst

View file
+View Decorators
+Python has a really interesting feature called function decorators.  This
+allow some really neat things for web applications.  Because each view in
+Flask is a function decorators can be used to inject additional
+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
+view that should only be used by people that are logged in to.  If a user
+goes to the site and is not logged in, he should be redirected to the
+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
+returns a function.  Pretty simple actually.  The only thing you have to
+keep in mind when implementing something like this is to update the
+`__name__`, `__module__` and some other attributes of a function.  This is
+often forgotten, but you don't have to do that by hand, there is a
+function for that that is used like a decorator (:func:`functools.wraps`).
+This example assumes that the login page is called ``'login'`` and that
+the current user is stored as `g.user` and `None` if there is no-one
+logged in::
+    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
+So how would you use that decorator now?  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::
+    @app.route('/secret_page')
+    @login_required
+    def secret_page():
+        pass
+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
+assuming you have set up a cache like mentioned in :ref:`caching-pattern`.
+Here an example cache function.  It generates the cache key from a
+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
+1. get the unique cache key for the current request base on the current
+   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
+    def cached(timeout=5 * 60, key='view/%s'):
+        def decorator(f):
+            @wraps(f)
+            def decorated_function(*args, **kwargs):
+                cache_key = key % request.path
+                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
+Notice that this assumes an instanciated `cache` object is available, see
+:ref:`caching-pattern` for more information.