from __future__ import unicode_literals from django.core.cache import InvalidCacheBackendError, caches from django.core.cache.utils import make_template_fragment_key from django.template import ( Library, Node, TemplateSyntaxError, VariableDoesNotExist, ) register = Library() class CacheNode(Node): def __init__(self, nodelist, expire_time_var, fragment_name, vary_on, cache_name): self.nodelist = nodelist self.expire_time_var = expire_time_var self.fragment_name = fragment_name self.vary_on = vary_on self.cache_name = cache_name def render(self, context): try: expire_time = self.expire_time_var.resolve(context) except VariableDoesNotExist: raise TemplateSyntaxError('"cache" tag got an unknown variable: %r' % self.expire_time_var.var) try: expire_time = int(expire_time) except (ValueError, TypeError): raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time) if self.cache_name: try: cache_name = self.cache_name.resolve(context) except VariableDoesNotExist: raise TemplateSyntaxError('"cache" tag got an unknown variable: %r' % self.cache_name.var) try: fragment_cache = caches[cache_name] except InvalidCacheBackendError: raise TemplateSyntaxError('Invalid cache name specified for cache tag: %r' % cache_name) else: try: fragment_cache = caches['template_fragments'] except InvalidCacheBackendError: fragment_cache = caches['default'] vary_on = [var.resolve(context) for var in self.vary_on] cache_key = make_template_fragment_key(self.fragment_name, vary_on) value = fragment_cache.get(cache_key) if value is None: value = self.nodelist.render(context) fragment_cache.set(cache_key, value, expire_time) return value @register.tag('cache') def do_cache(parser, token): """ This will cache the contents of a template fragment for a given amount of time. Usage:: {% load cache %} {% cache [expire_time] [fragment_name] %} .. some expensive processing .. {% endcache %} This tag also supports varying by a list of arguments:: {% load cache %} {% cache [expire_time] [fragment_name] [var1] [var2] .. %} .. some expensive processing .. {% endcache %} Optionally the cache to use may be specified thus:: {% cache .... using="cachename" %} Each unique set of arguments will result in a unique cache entry. """ nodelist = parser.parse(('endcache',)) parser.delete_first_token() tokens = token.split_contents() if len(tokens) < 3: raise TemplateSyntaxError("'%r' tag requires at least 2 arguments." % tokens[0]) if len(tokens) > 3 and tokens[-1].startswith('using='): cache_name = parser.compile_filter(tokens[-1][len('using='):]) tokens = tokens[:-1] else: cache_name = None return CacheNode(nodelist, parser.compile_filter(tokens[1]), tokens[2], # fragment_name can't be a variable. [parser.compile_filter(t) for t in tokens[3:]], cache_name, )