This is a decorator which will gets Django to try the cache before computing the result of a function. It automatically builds the cache key as a hash of the function name and inputs, and allows you to set whatever timeout you want.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | import hashlib
from django.core.cache import cache
def try_cache_first(timeout=300):
"""
Tries to get the output of the function from the cache firsts. Otherwise,
computes the function and stores the result in the cache.
You can also pass the timeout in seconds for the cached value. By default,
this is 5 minutes.
Example usage, which holds the value of expensive_function in the cache for
10 minutes:
@try_cache_first(timeout=600)
def expensive_function():
<do expensive stuff>
return result
All results are indexed by a hash of the function name and parameters,
so changes to function inputs should refresh the cache automatically.
"""
def decorator(func):
def wrapper(*args, **kwargs):
# Build keys from function name and arguments
caching_keys = [func.__name__]
if args is not None:
caching_keys.extend(args)
if kwargs is not None:
caching_keys.extend(sorted(kwargs.iteritems()))
# have to sort the caching keys because kwargs can be in random
# order which screws with hashing the inputs.
# Convert the keys to a big string and hash it
caching_keys = map(str, caching_keys)
cache_key = '_'.join(caching_keys)
cache_key = hashlib.sha512(cache_key).hexdigest()
cache_key = cache_key[:250] # max size of caching keys in memcached
# Fetch from cache
output = cache.get(cache_key)
if output is None:
output = func(*args, **kwargs)
cache.set(cache_key, output, timeout)
return output
return wrapper
return decorator
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 1 week ago
- Serializer factory with Django Rest Framework by julio 1 year, 4 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 5 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
nice, but what if the real result to be cached is None ? IMHO you shoud encode None-result differ than None-absence-in-cache...
#
Always use functools wraps(). It's a convenience function that makes the wrapper function (i.e., the return value from the decorator) look like the function it is wrapping. This involves copying/updating a bunch of the double underscore attributes specifically module, name, doc, and dict. Suppose that the decorated function (the func in this example) is named long_calc(). If you don't use wraps() then the name variable of the decorated function will be "wrapper" instead of "long_calc"! This might generate some bugs if you intend to use it's name variable.
from functools import wraps
def wrapper(args, *kwargs): ... return wraps(func)(wrapper)
#
Please login first before commenting.