Added docs on caching and decorators.
This commit is contained in:
parent
f419937d2c
commit
4559e4f0f1
3 changed files with 164 additions and 0 deletions
69
docs/patterns/caching.rst
Normal file
69
docs/patterns/caching.rst
Normal file
|
|
@ -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
|
||||
|
|
@ -17,6 +17,8 @@ end of the request, the database connection is closed again.
|
|||
sqlite3
|
||||
sqlalchemy
|
||||
fileuploads
|
||||
caching
|
||||
viewdecorators
|
||||
wtforms
|
||||
templateinheritance
|
||||
flashing
|
||||
|
|
|
|||
93
docs/patterns/viewdecorators.rst
Normal file
93
docs/patterns/viewdecorators.rst
Normal file
|
|
@ -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…
Add table
Add a link
Reference in a new issue