from django.core.cache import cache from django.template.defaultfilters import slugify from django.http import HttpResponse from django.conf import settings CACHE_KEY_PREFIX = settings.CACHE_MIDDLEWARE_KEY_PREFIX + 'cache-page:' def invalidate_cache(url, html5=None): """Does cache invalidation for a given URL""" cache.set(make_cache_key(url, html5), None) def invalidate_all_cached_pages(): """Invalidates all cached pages at once.""" keys = [key for key in cache._cache.keys() if key.startswith(CACHE_KEY_PREFIX)] cache.set_many(dict([(key, None) for key in keys])) def make_cache_key(url, html5=None): key = CACHE_KEY_PREFIX + slugify(url) if html5 == True: key = '.'.join([key, 'html5']) elif html5 == False: key = '.'.join([key, 'html4']) return key def cache_page(arg=None): # 1 day as default """This decorator works in replacement to Django's decorator with same name, but it doesn't use Django's middleware system to make cache key, so, it uses its own logic to do it and make possible invalidate cache. This decorator shouldn't be used to views with user-based data.""" default_exp = 60 * 30 # 30 minutes def _wrapper(func): def _inner(request, *args, **kwargs): key = make_cache_key(request.get_full_path(), getattr(request, 'supports_html5', None)) content = cache.get(key, None) if not content: resp = func(request, *args, **kwargs) content = resp.content # Only stores in cache if is a regular HttpResponse returning text/html if resp.__class__.__name__ == 'HttpResponse' and\ getattr(resp, 'mimetype', 'text/html') == 'text/html': cache.set(key, content, expiration) else: return resp return HttpResponse(content) return _inner if callable(arg): expiration = default_exp return _wrapper(arg) else: expiration = arg or default_exp return _wrapper