# Per request cache middleware # Provides a simple cache dictionary only existing for the request's lifetime # The middleware provides a threadsafe LocMemCache which can be used just # like any other django cache facility from functools import wraps from threading import currentThread from django.core.cache.backends.locmem import LocMemCache try: from django.utils.deprecation import MiddlewareMixin except ImportError: # djang0 < 1.10 class MiddlewareMixin(object): pass _request_cache = {} _installed_middleware = False def get_request_cache(): assert _installed_middleware, 'RequestCacheMiddleware not loaded' return _request_cache[currentThread()] def clear_request_cache(): _request_cache[currentThread()].clear() class RequestCache(LocMemCache): def __init__(self): name = 'locmemcache@%i' % hash(currentThread()) params = dict() super(RequestCache, self).__init__(name, params) class RequestCacheMiddleware(MiddlewareMixin): def __init__(self, get_response=None): global _installed_middleware self.get_response = get_response _installed_middleware = True def process_request(self, request): cache = _request_cache.get(currentThread()) or RequestCache() _request_cache[currentThread()] = cache cache.clear() class request_cache(object): """ A decorator for use around functions that should be cached for the current request. Use like this: @request_cache() def cached(name): print("My name is %s and I'm cached" % name) @request_cache(keyfn=lambda p: p['id']) def cached(param): print("My id is %s" % p['id']) If no keyfn is provided the decorator expects the args to be hashable. """ def __init__(self, keyfn=None): self.keyfn = keyfn or (lambda *args: hash(args)) def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): cache = get_request_cache() cachekey = self.keyfn(*args) cacheval = cache.get(cachekey, 'expired') if not cacheval == 'expired': return cacheval result = func(*args, **kwargs) cache.set(cachekey, result) return result return wrapper