Commit 8688f03e authored by Curtis Maloney's avatar Curtis Maloney Committed by Tim Graham
Browse files

Fixed #20945 -- Allowed cache tag to use a specific cache.

parent 4ce5c119
Loading
Loading
Loading
Loading
+31 −3
Original line number Diff line number Diff line
from __future__ import unicode_literals

from django.core.cache import get_cache, InvalidCacheBackendError
from django.core.cache.utils import make_template_fragment_key
from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist
from django.core.cache import cache

register = Library()

try:
    default_cache = get_cache('template_fragments')
except InvalidCacheBackendError:
    from django.core.cache import cache as default_cache


class CacheNode(Node):
    def __init__(self, nodelist, expire_time_var, fragment_name, vary_on):
    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:
@@ -22,6 +29,17 @@ class CacheNode(Node):
            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:
                cache = get_cache(cache_name)
            except InvalidCacheBackendError:
                raise TemplateSyntaxError('Invalid cache name specified for cache tag: %r' % cache_name)
        else:
            cache = default_cache
        vary_on = [var.resolve(context) for var in self.vary_on]
        cache_key = make_template_fragment_key(self.fragment_name, vary_on)
        value = cache.get(cache_key)
@@ -50,6 +68,9 @@ def do_cache(parser, token):
            .. 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',))
@@ -57,7 +78,14 @@ def do_cache(parser, 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:]])
        [parser.compile_filter(t) for t in tokens[3:]],
        cache_name,
    )
+5 −0
Original line number Diff line number Diff line
@@ -396,6 +396,11 @@ Templates
  <naive_vs_aware_datetimes>` ``datetime`` instances performing the expected
  rendering.

* The :ttag:`cache` tag will now try to use the cache called
  "template_fragments" if it exists and fall back to using the default cache
  otherwise. It also now accepts an optional ``using`` keyword argument to
  control which cache it uses.

Requests
^^^^^^^^

+13 −0
Original line number Diff line number Diff line
@@ -652,6 +652,19 @@ equivalent:
This feature is useful in avoiding repetition in templates. You can set the
timeout in a variable, in one place, and just reuse that value.

.. versionadded:: 1.7

    By default, the cache tag will try to use the cache called
    "template_fragments". If no such cache exists, it will fall back to using
    the default cache. You may select an alternate cache backend to use with
    the ``using`` keyword argument, which must be the last argument to the tag.

.. code-block:: html+django

    {% cache 300 local-thing ...  using="localcache" %}

It is considered an error to specify a cache name that is not configured.

.. function:: django.core.cache.utils.make_template_fragment_key(fragment_name, vary_on=None)

If you want to obtain the cache key used for a cached fragment, you can use
+36 −1
Original line number Diff line number Diff line
@@ -478,6 +478,42 @@ class TemplateRegressionTests(TestCase):
        cachenode = t.nodelist[1]
        self.assertEqual(cachenode.fragment_name, 'regression_20130')

    @override_settings(CACHES={
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'default',
        },
        'template_fragments': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'fragments',
        },
    })
    def test_cache_fragment_cache(self):
        """
        When a cache called "template_fragments" is present, the cache tag
        will use it in preference to 'default'
        """
        t1 = Template('{% load cache %}{% cache 1 fragment %}foo{% endcache %}')
        t2 = Template('{% load cache %}{% cache 1 fragment using="default" %}bar{% endcache %}')

        ctx = Context()
        o1 = t1.render(ctx)
        o2 = t2.render(ctx)

        self.assertEqual(o1, 'foo')
        self.assertNotEqual(o1, o2)

    def test_cache_missing_backend(self):
        """
        When a cache that doesn't exist is specified, the cache tag will
        raise a TemplateSyntaxError
        '"""
        t = Template('{% load cache %}{% cache 1 backend using="unknown" %}bar{% endcache %}')

        ctx = Context()
        with self.assertRaises(TemplateSyntaxError):
            t.render(ctx)

    def test_ifchanged_render_once(self):
        """ Test for ticket #19890. The content of ifchanged template tag was
        rendered twice."""
@@ -1736,7 +1772,6 @@ class TemplateTests(TransRealMixin, TestCase):
            # Test whitespace in filter arguments
            'cache18': ('{% load cache custom %}{% cache 2|noop:"x y" cache18 %}cache18{% endcache %}', {}, 'cache18'),


            ### AUTOESCAPE TAG ##############################################
            'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"),
            'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"),