mirror of https://github.com/mitsuhiko/flask.git
Armin Ronacher
15 years ago
3 changed files with 164 additions and 0 deletions
@ -0,0 +1,69 @@
|
||||
.. _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 |
@ -0,0 +1,93 @@
|
||||
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. |
Loading…
Reference in new issue