summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomek Paczkowski <tomek@hauru.eu>2013-02-24 12:58:02 +0100
committerHonza Kral <honza.kral@gmail.com>2013-02-24 14:32:45 +0100
commit99edbe0e279166db82caaf545ef92d5446a6a07e (patch)
tree495785ea6bca5f1c94881d624b622b1d6ea93b30
parentb9cc61021a0db1e5b41e61d3e53180e4fc618f9c (diff)
Fixed #19253 -- Extracted template cache key building logic
Introduced a public function django.core.cache.utils.make_template_fragment_key Thanks @chrismedrela for fruitful cooperation.
-rw-r--r--AUTHORS2
-rw-r--r--django/core/cache/utils.py15
-rw-r--r--django/templatetags/cache.py10
-rw-r--r--docs/topics/cache.txt17
-rw-r--r--tests/regressiontests/cache/tests.py23
5 files changed, 60 insertions, 7 deletions
diff --git a/AUTHORS b/AUTHORS
index 9089f7db90..5f9cb980c6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -382,6 +382,7 @@ answer newbie questions, and generally made Django that much better:
Paul McLanahan <paul@mclanahan.net>
Tobias McNulty <http://www.caktusgroup.com/blog>
Andrews Medina <andrewsmedina@gmail.com>
+ Christoph Mędrela <chris.medrela@gmail.com>
Zain Memon
Christian Metts
michal@plovarna.cz
@@ -418,6 +419,7 @@ answer newbie questions, and generally made Django that much better:
Christian Oudard <christian.oudard@gmail.com>
oggie rob <oz.robharvey@gmail.com>
oggy <ognjen.maric@gmail.com>
+ Tomek Paczkowski <tomek@hauru.eu>
Jens Page
Jay Parlar <parlar@gmail.com>
Carlos Eduardo de Paula <carlosedp@gmail.com>
diff --git a/django/core/cache/utils.py b/django/core/cache/utils.py
new file mode 100644
index 0000000000..4310825ad4
--- /dev/null
+++ b/django/core/cache/utils.py
@@ -0,0 +1,15 @@
+from __future__ import absolute_import, unicode_literals
+
+import hashlib
+from django.utils.encoding import force_bytes
+from django.utils.http import urlquote
+
+TEMPLATE_FRAGMENT_KEY_TEMPLATE = 'template.cache.%s.%s'
+
+
+def make_template_fragment_key(fragment_name, vary_on=None):
+ if vary_on is None:
+ vary_on = ()
+ key = ':'.join([urlquote(var) for var in vary_on])
+ args = hashlib.md5(force_bytes(key))
+ return TEMPLATE_FRAGMENT_KEY_TEMPLATE % (fragment_name, args.hexdigest())
diff --git a/django/templatetags/cache.py b/django/templatetags/cache.py
index bb21b91c6a..8c061ca4c6 100644
--- a/django/templatetags/cache.py
+++ b/django/templatetags/cache.py
@@ -1,10 +1,8 @@
from __future__ import unicode_literals
-import hashlib
+from django.core.cache.utils import make_template_fragment_key
from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist
from django.core.cache import cache
-from django.utils.encoding import force_bytes
-from django.utils.http import urlquote
register = Library()
@@ -24,10 +22,8 @@ class CacheNode(Node):
expire_time = int(expire_time)
except (ValueError, TypeError):
raise TemplateSyntaxError('"cache" tag got a non-integer timeout value: %r' % expire_time)
- # Build a key for this fragment and all vary-on's.
- key = ':'.join([urlquote(var.resolve(context)) for var in self.vary_on])
- args = hashlib.md5(force_bytes(key))
- cache_key = 'template.cache.%s.%s' % (self.fragment_name, args.hexdigest())
+ 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)
if value is None:
value = self.nodelist.render(context)
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index e345b89dcd..6b6d57511a 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -639,6 +639,23 @@ 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.
+.. 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
+``make_template_fragment_key``. ``fragment_name`` is the same as second argument
+to the ``cache`` template tag; ``vary_on`` is a list of all additional arguments
+passed to the tag. This function can be useful for invalidating or overwriting
+a cached item, for example:
+
+.. code-block:: python
+
+ >>> from django.core.cache import cache
+ >>> from django.core.cache.utils import make_template_fragment_key
+ # cache key for {% cache 500 sidebar username %}
+ >>> key = make_template_fragment_key('sidebar', [username])
+ >>> cache.delete(key) # invalidates cached template fragment
+
+
The low-level cache API
=======================
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index 17d17f7fdd..d5d538319a 100644
--- a/tests/regressiontests/cache/tests.py
+++ b/tests/regressiontests/cache/tests.py
@@ -20,6 +20,7 @@ from django.core.cache import get_cache
from django.core.cache.backends.base import (CacheKeyWarning,
InvalidCacheBackendError)
from django.db import router, transaction
+from django.core.cache.utils import make_template_fragment_key
from django.http import (HttpResponse, HttpRequest, StreamingHttpResponse,
QueryDict)
from django.middleware.cache import (FetchFromCacheMiddleware,
@@ -1809,3 +1810,25 @@ class TestEtagWithAdmin(TestCase):
response = self.client.get('/test_admin/admin/')
self.assertEqual(response.status_code, 200)
self.assertTrue(response.has_header('ETag'))
+
+
+class TestMakeTemplateFragmentKey(TestCase):
+ def test_without_vary_on(self):
+ key = make_template_fragment_key('a.fragment')
+ self.assertEqual(key, 'template.cache.a.fragment.d41d8cd98f00b204e9800998ecf8427e')
+
+ def test_with_one_vary_on(self):
+ key = make_template_fragment_key('foo', ['abc'])
+ self.assertEqual(key,
+ 'template.cache.foo.900150983cd24fb0d6963f7d28e17f72')
+
+ def test_with_many_vary_on(self):
+ key = make_template_fragment_key('bar', ['abc', 'def'])
+ self.assertEqual(key,
+ 'template.cache.bar.4b35f12ab03cec09beec4c21b2d2fa88')
+
+ def test_proper_escaping(self):
+ key = make_template_fragment_key('spam', ['abc:def%'])
+ self.assertEqual(key,
+ 'template.cache.spam.f27688177baec990cdf3fbd9d9c3f469')
+