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