cache_smart template tag

 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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
from django.template import Library, Node, TemplateSyntaxError
from django.template import resolve_variable
from django.core.cache import cache
from django.utils.encoding import force_unicode

register = Library()

class SmartCacheNode(Node):
    STALE_REFRESH = 1
    STALE_CREATED = 2
    def __init__(self, nodelist, expire_time, fragment_name, vary_on):
        self.nodelist = nodelist
        self.stale_time = expire_time
        self.expire_time = expire_time + 300 # create a window to refresh
        self.fragment_name = fragment_name
        self.vary_on = vary_on

    def render(self, context):
        # Build a unicode key for this fragment and all vary-on's.
        cache_key = u':'.join([self.fragment_name] + \
            [force_unicode(resolve_variable(var, context)) for var in self.vary_on])
        cache_key_stale = cache_key + '.stale'
        value = cache.get(cache_key)
        stale = cache.get(cache_key_stale)
        if stale is None:
            cache.set(cache_key_stale, self.STALE_REFRESH, 30) # lock
            value = None # force refresh
        if value is None:
            value = self.nodelist.render(context)
            cache.set(cache_key, value, self.expire_time)
            cache.set(cache_key_stale, self.STALE_CREATED, self.stale_time) # reset        return value


def do_smart_cache(parser, token):
    """
    This will cache the contents of a template fragment for a given amount
    of time, but with the extra bonus of limiting the dog-pile/stampeding
    effect.

    You can easily replace the default template cache, just change the load
    statement from ``{% load cache %}`` to ``{% load cache_smart %}``.

    Usage::

        {% load cache_smart %}
        {% cache [expire_time] [fragment_name] %}
            .. some expensive processing ..
        {% endcache %}

    This tag also supports varying by a list of arguments::

        {% load cache_smart %}
        {% cache [expire_time] [fragment_name] [var1] [var2] .. %}
            .. some expensive processing ..
        {% endcache %}

    Each unique set of arguments will result in a unique cache entry.
    """
    nodelist = parser.parse(('endcache',))
    parser.delete_first_token()
    tokens = token.contents.split()
    if len(tokens) < 3:
        raise TemplateSyntaxError(u"'%r' tag requires at least 2 arguments." % tokens[0])
    try:
        expire_time = int(tokens[1])
    except ValueError:
        raise TemplateSyntaxError(u"First argument to '%r' must be an integer (got '%s')." % (tokens[0], tokens[1]))
    return SmartCacheNode(nodelist, expire_time, tokens[2], tokens[3:])

register.tag('cache', do_smart_cache)

More like this

  1. Use memcached to throttle POSTs by coleifer 3 years, 11 months ago
  2. Extended db cache backend with 'filter() / LIKE' support (and now scheduled cache clean!) by sleepycal 4 years, 3 months ago
  3. Default Template Loading by nirvdrum 6 years, 8 months ago
  4. Silently-failing include tag by brutasse 3 years, 10 months ago
  5. MintCache by gfranxman 7 years ago

Comments

(Forgotten your password?)