Usage

Caching View Functions

To cache view functions you will use the cached() decorator. This decorator will use request.path by default for the cache_key:

@app.route("/")
@cache.cached(timeout=50)
def index():
    return render_template('index.html')

The cached decorator has another optional argument called unless. This argument accepts a callable that returns True or False. If unless returns True then it will bypass the caching mechanism entirely.

To dynamically determine the timeout within the view, you can return CachedResponse, a subclass of flask.Response:

@app.route("/")
@cache.cached()
def index():
    return CachedResponse(
        response=make_response(render_template('index.html')),
        timeout=50,
    )

Warning

When using cached on a view, take care to put it between Flask’s @route decorator and your function definition. Example:

@app.route('/')
@cache.cached(timeout=50)
def index():
    return 'Cached for 50s'

If you reverse both decorators, what will be cached is the result of @route decorator, and not the result of your view function.

Deleting Cached Views

When you want to remove the value of a cached view you can use delete_cached() instead of delete(). delete_cached(). builds the key of the decorated view and deletes the cache in one go:

@app.route("/user/<name>")
@cache.cached(timeout=50)
def user(name):
    return render_template("user.html", name=name)

cache.delete_cached(user, "/user/Fred")

Outside of a request context the view doesn’t know which request was used to build the cache key. So to make it work outside of a request context you have to pass the path. You can also use the view function and the named arguments of the view in which case the path is built using url_for():

cache.delete_cached(user, name="bob")

When you cache views with query_string=True you also have to pass the query string/args because otherwise the cache key cannot be built:

@app.route("/works")
@cache.cached(timeout=50, query_string=True)
def works():
    return do_search(request.args)

cache.delete_cached(works, "/works", "limit=15&mock=true")

You can use either pass the query string as a string, a mapping or an iterable of (key, value) pairs:

cache.delete_cached(works, "/works", {"limit": 15, "mock": "true"})

Additionally, path and query_args are also supported by the views make_cache_key():

key = works.make_cache_key(path="/works", query_args={"limit": 15})

Note

Inside a request context the view arguments are used as-is, so a view argument named path or query_args is not mistaken for the arguments above. Outside of a request context the two cannot be told apart, so such a view has to build its key from within the request context.

Caching Pluggable View Classes

Flask’s pluggable view classes are also supported. To cache them, use the same cached() decorator on the dispatch_request method:

from flask.views import View

class MyView(View):
    @cache.cached(timeout=50)
    def dispatch_request(self):
        return 'Cached for 50s'

Caching Other Functions

Using the same @cached decorator you are able to cache the result of other non-view related functions. The only stipulation is that you replace the key_prefix, otherwise it will use the request.path cache_key. Keys control what should be fetched from the cache. If, for example, a key does not exist in the cache, a new key-value entry will be created in the cache. Otherwise the value (i.e. the cached result) of the key will be returned:

@cache.cached(timeout=50, key_prefix='all_comments')
def get_all_comments():
    comments = do_serious_dbio()
    return [x.author for x in comments]

cached_comments = get_all_comments()

Make Custom Cache Key

Sometimes you want to define your cache key for each route. Using the same @cached decorator you are able to specify how this key is generated. This might be useful when the key for cache should not be just the default key_prefix, but has to be derived from other parameters in a request. An example usecase would be for caching POST routes, where the cache key should be derived from the data in that request, rather than just the route/view itself.

make_cache_key can be used to specify such a function. The function should return a string which should act like the key to the required value that is being cached:

def make_key():
   """A function which is called to derive the key for a computed value.
      The key in this case is the concat value of all the json request
      parameters. Other strategy could to use any hashing function.
   :returns: unique string for which the value should be cached.
   """
   user_data = request.get_json()
   return ",".join([f"{key}={value}" for key, value in user_data.items()])

@app.route("/hello", methods=["POST"])
@cache.cached(timeout=60, make_cache_key=make_key)
def some_func():
   ....

Caching Routes With Multiple HTTP Methods

The default cache key is built from request.path alone, so a route that accepts more than one method serves all of them from the same entry:

@app.route("/foo", methods=["GET", "POST"])
@cache.cached(timeout=50)
def foo():
    return f"body for {request.method}"

Whichever method is requested first is stored under view//foo and returned for the others until the entry expires.

Pass a callable key_prefix to put the method into the key:

from flask import request

def method_key():
    return f"view/{request.method}{request.path}"

@app.route("/foo", methods=["GET", "POST"])
@cache.cached(timeout=50, key_prefix=method_key)
def foo():
    return f"body for {request.method}"

Each method now has its own entry, view/GET/foo and view/POST/foo. The callable takes no arguments and is called inside the request context, so it can read anything from request. It replaces the whole prefix, %s is not substituted.

This also works with query_string=True:

@app.route("/works", methods=["GET", "POST"])
@cache.cached(timeout=50, query_string=True, key_prefix=method_key)
def works():
    return do_search(request.args)

When the key depends on the request body rather than the method use make_cache_key instead. See Make Custom Cache Key.

Note

Flask adds HEAD to every route that allows GET and strips the body from HEAD responses itself, so both methods can share one entry. Map HEAD onto GET to avoid caching the same body twice:

def method_key():
    method = "GET" if request.method == "HEAD" else request.method
    return f"view/{method}{request.path}"

However, only do this when the view builds the same response for both methods.

Warning

A callable key_prefix replaces the path argument of delete_cached() so you have to delete the entry either from a request context:

with app.test_request_context("/foo", method="POST"):
    cache.delete_cached(foo)

or pass the key to delete() yourself:

cache.delete("view/POST/foo")

With query_string=True only the path comes from the callable, the query_args argument still applies:

with app.test_request_context("/works"):
    cache.delete_cached(works, query_args={"limit": 15})

Memoization

See memoize()

In memoization, the functions arguments are also included into the cache_key.

Note

With functions that do not receive arguments, cached() and memoize() are effectively the same.

Memoize is also designed for methods, since it will take into account the identity of the self or cls argument as part of the cache key. By default this identity is derived from repr(obj) (or obj.__caching_id__() if the object provides one). It is not based on Python’s built-in id(), so two distinct instances that share the same repr (for example two ORM objects loaded for the same row) will share a memoize cache entry.

The theory behind memoization is that if you have a function you need to call several times in one request, it would only be calculated the first time that function is called with those arguments. For example, an sqlalchemy object that determines if a user has a role. You might need to call this function many times during a single request. To keep from hitting the database every time this information is needed you might do something like the following:

class Person(db.Model):
    @cache.memoize(50)
    def has_membership(self, role_id):
        return Group.query.filter_by(user=self, role_id=role_id).count() >= 1

Warning

On a staticmethod or a classmethod, @staticmethod or @classmethod the route decorator must be applied at the top of the decorator stack (visually first, logically last) following the same logic as @route and @cached above:

class Person(db.Model):
    @staticmethod
    @cache.memoize(50)
    def calc_budget():
        return do_serious_dbio()

Warning

Using mutable objects (classes, etc) as part of the cache key can become tricky. It is suggested to not pass in an object instance into a memoized function. However, the memoize does perform a repr() on the passed in arguments so that if the object has a __repr__ function that returns a uniquely identifying string for that object, that will be used as part of the cache key.

For example, an sqlalchemy person object that returns the database id as part of the unique identifier:

class Person(db.Model):
    def __repr__(self):
        return "%s(%s)" % (self.__class__.__name__, self.id)

Deleting Memoize Cache

Changelog

Added in version 0.2.

You might need to delete the cache on a per-function basis. Using the above example, lets say you change the user’s permissions and assign them to a role, but now you need to re-calculate if they have certain memberships or not. You can do this with the delete_memoized() function:

cache.delete_memoized(user_has_membership)

Note

If only the function name is given as parameter, all the memoized versions of it will be invalidated. However, you can delete specific cache by providing the same parameter values as when caching. In following example only the user-role cache is deleted:

user_has_membership('demo', 'admin')
user_has_membership('demo', 'user')

cache.delete_memoized(user_has_membership, 'demo', 'user')

Warning

If a classmethod is memoized, you must provide the class as the first *args argument.

class Foobar(object):
    @classmethod
    @cache.memoize(5)
    def big_foo(cls, a, b):
        return a + b + random.randrange(0, 100000)

cache.delete_memoized(Foobar.big_foo, Foobar, 5, 2)

Memoized methods are stored per instance, so how much is deleted depends on whether the method is reached through an instance or through the class:

class Adder(object):
    @cache.memoize(5)
    def add(self, b):
        return b + random.random()

adder1 = Adder()
adder2 = Adder()

# only the calls made on adder1, adder2 keeps its cache
cache.delete_memoized(adder1.add)

# every instance
cache.delete_memoized(Adder.add)

# only ``adder1.add(3)``
cache.delete_memoized(adder1.add, 3)

Instances are told apart by their repr(), which can be overridden by defining a __caching_id__ method, for example to key on a user id.

Warning

A method reached through the class does not know which instance to delete the cache for, so the instance has to be passed as the first *args argument, the same way a class is passed for a classmethod:

cache.delete_memoized(Adder.add, adder1, 3)

Note

The delete_memoized() attribute that memoize() puts on the decorated function takes no arguments and always clears every instance, even when it is called through one:

adder1.add.delete_memoized()  # same as cache.delete_memoized(Adder.add)

Decorator Options

cached() and memoize() share most of their optional arguments. query_string and response_hit_indication are accepted by cached() only, make_name and args_to_ignore by memoize() only.

unless

A callable that bypasses the cache entirely while it returns True. The decorated function runs and its result is returned without the cache being read or written:

@app.route("/")
@cache.cached(timeout=50, unless=lambda: current_user.is_authenticated)
def index():
    return render_template('index.html')

If the callable accepts arguments it is called with the decorated function followed by the call’s own arguments, otherwise it is called with none.

forced_update

A callable that refreshes the cached value while it returns True, even if the entry has not expired yet. Useful for background renewal:

@app.route("/")
@cache.cached(timeout=50, forced_update=lambda: feature_flags["fresh"])
def index():
    return render_template('index.html')

If the callable accepts arguments it is called with the call’s own arguments, otherwise it is called with none. Unlike unless, the result is still written to the cache.

is_stale

Changelog

Added in version 2.5.0.

A callable that accepts the cached value as argument and checks whether the value is stale. If it returns True the cached value will be recomputed. Unlike forced_update it runs only on a cache hit:

def is_outdated(cached_obj):
    return get_last_data_updated() > cached_obj.last_update

@cache.memoize(timeout=86400, is_stale=is_outdated)
def build_report(user_id):
    return Report(user_id)

If the callable accepts more than one argument, the calls own arguments are passed after the cached value:

def is_stale(report, user_id):
    return report.generated_at < last_import(user_id)

@cache.memoize(timeout=86400, is_stale=is_stale)
def build_report(user_id):
    return Report(user_id)

forced_update and is_stale can be combined. forced_update is checked first and skips the cache read entirely.

response_filter

A callable invoked with the return value after the decorated function has run. If it returns False the value is not stored. Use it to keep failures out of the cache:

def only_success(response):
    return response.status_code == 200

@app.route("/")
@cache.cached(timeout=50, response_filter=only_success)
def index():
    return render_template('index.html')
Changelog

Changed in version 2.5.0: An HTTPException raised by a view for example through flask.abort() is cached like a returned response and re-raised on a cache hit, so errorhandler() functions still run. The filter is called with the exception’s response.

Warning

A view that aborts because of a server problem (i.e. status code 503) keeps returning that error until the entry expires. Use response_filter to keep such a status code out of the cache.

In case a view returns just a plain string (has no status_code), use a default value for the response. For example:

def not_server_error(response):
    return getattr(response, "status_code", 200) < 500

@app.route("/article/<slug>")
@cache.cached(timeout=50, response_filter=not_server_error)
def article(slug):
    if not backend.healthy():
        abort(503)  # raised again on every request

    article = load(slug)
    if article is None:
        abort(404)  # cached and re-raised for 50 seconds

    return render_template("article.html", article=article)

cache_none

A cached None is indistinguishable from a cache miss, so by default the decorated function runs again on every call. Set cache_none to True to store None and use an extra key existence check to tell the two apart.

Warning

This adds a second round trip to the backend, and can still return None wrongly if a concurrent call writes the key between the two calls. Returning a sentinel value instead is usually the better option.

source_check

Include the decorated function’s source code in the cache key, so that editing the body invalidates values cached by the previous version even when the arguments are unchanged:

@cache.memoize(timeout=50, source_check=True)
def add(a, b):
    return a + b

Defaults to the CACHE_SOURCE_CHECK configuration value, which is False. This is meant for development, where a stale value from an older revision of a function is confusing. It calls inspect.getsource() on every call, so leave it off in production.

hash_method

The hash constructor used when building cache keys. Defaults to the CACHE_HASH_METHOD configuration value, which is hashlib.sha256(). Set it application wide:

app.config["CACHE_HASH_METHOD"] = hashlib.sha512

or override it for a single decorator:

@cache.memoize(timeout=50, hash_method=hashlib.sha512)
def add(a, b):
    return a + b

Warning

The cache key is derived from this hash, so changing it makes every existing cached entry unreachable. The stale entries are not deleted; they remain in the backend until they expire.

Do not confuse this with CACHE_FILE_HASH_METHOD, which the FileSystemCache backend uses to name the files it writes. The two are independent.

query_string

cached() only. Build the cache key from the request’s query string instead of key_prefix. The arguments are sorted before hashing, so ?limit=10&offset=20 and ?offset=20&limit=10 share one entry:

@app.route("/search")
@cache.cached(timeout=50, query_string=True)
def search():
    return do_search(request.args)

Deleting such an entry needs the query string as well, see Deleting cached views.

response_hit_indication

cached() only. When True, responses that were served from the cache carry a hit_cache header. Responses produced by running the view do not:

@app.route("/")
@cache.cached(timeout=50, response_hit_indication=True)
def index():
    return render_template('index.html')
Changelog

Added in version 2.3.0.

make_name

memoize() only. A callable that receives the name of the decorated function and returns the name to use in the cache key. Without it the function name is used.

args_to_ignore

memoize() only. Names of arguments to leave out of the cache key, so that calls differing only in those arguments share an entry:

@cache.memoize(timeout=50, args_to_ignore=["session"])
def get_user(session, user_id):
    return session.query(User).get(user_id)
Changelog

Added in version 1.10.

Caching Jinja2 Snippets

Usage:

{% cache [timeout [,[key1, [key2, ...]]]] %}
...
{% endcache %}

By default, the value of “path to template file” + “block start line” is used as the cache key. Also, the key name can be set manually. Keys are concatenated together into a single string, that can be used to avoid the same block evaluating in different templates.

Set the timeout to None for no timeout, but with custom keys:

{% cache None, "key" %}
...
{% endcache %}

Set timeout to del to delete cached value:

{% cache 'del', key1 %}
...
{% endcache %}

If keys are provided, you may easily generate the template fragment key and delete it from outside of the template context:

from flask_caching import make_template_fragment_key
key = make_template_fragment_key("key1", vary_on=["key2", "key3"])
cache.delete(key)

Considering we have render_form_field and render_submit macros:

{% cache 60*5 %}
<div>
    <form>
    {% render_form_field(form.username) %}
    {% render_submit() %}
    </form>
</div>
{% endcache %}

Clearing Cache

See clear(). To delete the entry of a single view see Deleting cached views.

Here’s an example script to empty your application’s cache:

from flask_caching import Cache

from yourapp import app, your_cache_config

cache = Cache()


def main():
    cache.init_app(app, config=your_cache_config)

    with app.app_context():
        cache.clear()

if __name__ == '__main__':
    main()

Warning

Some backend implementations do not support completely clearing the cache. Also, if you’re not using a key prefix, some implementations (e.g. Redis) will flush the whole database. Make sure you’re not storing any other data in your caching database.

Explicitly Caching Data

Data can be cached explicitly by using the proxy methods like Cache.set(), and Cache.get() directly. There are many other proxy methods available via the Cache class.

For example:

@app.route("/html")
@app.route("/html/<foo>")
def html(foo=None):
    if foo is not None:
        cache.set("foo", foo)
    bar = cache.get("foo")
    return render_template_string(
        "<html><body>foo cache: {{bar}}</body></html>", bar=bar
    )

Subclassing Cache

The proxy methods are the extension’s own entry point to the backend, so overriding them in a Cache subclass also affects the caching done by cached() and memoize(). This makes it possible to add tracing, metrics or logging in one place and have it cover both explicit calls and the decorators:

class InstrumentedCache(Cache):
    def get(self, *args, **kwargs):
        with tracer.trace("cache.get"):
            return super().get(*args, **kwargs)

    def set(self, *args, **kwargs):
        with tracer.trace("cache.set"):
            return super().set(*args, **kwargs)


cache = InstrumentedCache(app, config={"CACHE_TYPE": "SimpleCache"})

Note that memoize() also uses get_many() and set_many() for its internal version keys, so an override will see that bookkeeping traffic as well. A cache miss makes two such round trips: one to read the version before building the key, and one after the entry is written to update the version keys expiry.

To reach backend specific functionality that Cache does not proxy, use the Cache.cache property instead of subclassing.