Armin Ronacher avatar Armin Ronacher committed f05c571

Added docs on caching and decorators.

Comments (0)

Files changed (3)

docs/patterns/caching.rst

+.. _caching-pattern:
+
+Caching
+=======
+
+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
+time.
+
+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(['127.0.0.1:11211'])
+
+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

docs/patterns/index.rst

    sqlite3
    sqlalchemy
    fileuploads
+   caching
+   viewdecorators
    wtforms
    templateinheritance
    flashing

docs/patterns/viewdecorators.rst

+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.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.