Merge branch 'master' into module-support
This commit is contained in:
commit
13933eeaf8
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
|
sqlite3
|
||||||
sqlalchemy
|
sqlalchemy
|
||||||
fileuploads
|
fileuploads
|
||||||
|
caching
|
||||||
|
viewdecorators
|
||||||
wtforms
|
wtforms
|
||||||
templateinheritance
|
templateinheritance
|
||||||
flashing
|
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