Changeset 108
- Timestamp:
- 11/07/09 18:23:31 (10 months ago)
- Location:
- trunk/courant/core/caching
- Files:
-
- 9 added
- 3 modified
-
__init__.py (modified) (1 diff)
-
actions.py (added)
-
management (added)
-
management/__init__.py (added)
-
management/commands (added)
-
management/commands/__init__.py (added)
-
management/commands/prune_cachedobjects.py (added)
-
migrations (added)
-
migrations/0001_initial.py (added)
-
migrations/__init__.py (added)
-
models.py (modified) (1 diff)
-
templatetags/smart_cache.py (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/courant/core/caching/__init__.py
r1 r108 1 from actions import * -
trunk/courant/core/caching/models.py
r1 r108 1 # filler so courant.core.caching gets recognized as installable app 1 from django.db import models 2 from django.db.models.signals import post_save 3 from django.core.cache import cache 4 from django.contrib.contenttypes.models import ContentType 5 from django.contrib.contenttypes import generic 6 from django_extensions.db.fields import ModificationDateTimeField 7 from django.conf import settings 8 9 class CachedObject(models.Model): 10 cache_key = models.CharField(max_length=255) 11 url = models.CharField(max_length=255) 12 content_type = models.ForeignKey(ContentType) 13 object_id = models.PositiveIntegerField() 14 content_object = generic.GenericForeignKey('content_type', 'object_id') 15 modified_at = ModificationDateTimeField() 16 17 def clear_obj_cache(obj): 18 try: 19 obj_caches = CachedObject.objects.filter(content_type=ContentType.objects.get_for_model(obj), 20 object_id=obj.pk) 21 urls = [] 22 for obj_cache in obj_caches: 23 # mark object's cache as stale so it will be safely regenerated 24 cache.delete("%s.stale" % obj_cache.cache_key) 25 26 # mark URL as needing cache clearing 27 urls.append(obj_cache.url) 28 29 # delete caches for all marked URLs 30 # since full-page caches don't use anti-dogpiling, we delete the caches themselves 31 for url in set(urls): 32 key = "%s-%s" % (settings.CACHE_KEY_PREFIX, url) 33 print "Deleting cache: %s" % key 34 cache.delete(key) 35 except: 36 pass 37 38 def clear_obj_cache_signal(sender, instance, **kwargs): 39 clear_obj_cache(instance) 40 post_save.connect(clear_obj_cache_signal) -
trunk/courant/core/caching/templatetags/smart_cache.py
r1 r108 3 3 from django.core.cache import cache 4 4 from django.utils.encoding import force_unicode 5 from django.contrib.contenttypes.models import ContentType 6 7 from courant.core.caching.models import CachedObject 5 8 6 9 register = Library() … … 13 16 STALE_CREATED = 2 14 17 15 def __init__(self, nodelist, expire_time, fragment_name, vary_on ):18 def __init__(self, nodelist, expire_time, fragment_name, vary_on, cache_obj=None): 16 19 self.nodelist = nodelist 17 20 self.stale_time = expire_time … … 19 22 self.fragment_name = fragment_name 20 23 self.vary_on = vary_on 24 self.cache_obj = cache_obj 21 25 22 26 def render(self, context): … … 31 35 value = None # force refresh 32 36 if value is None: 37 context.push() 38 context['cache_key'] = cache_key 33 39 value = self.nodelist.render(context) 40 context.pop() 34 41 cache.set(cache_key, value, self.expire_time) 35 42 cache.set(cache_key_stale, self.STALE_CREATED, self.stale_time) 43 if self.cache_obj: 44 obj = resolve_variable(self.cache_obj, context) 45 co, created = CachedObject.objects.get_or_create(url=context['request'].get_full_path(), 46 content_type=ContentType.objects.get_for_model(obj), 47 object_id=obj.pk, 48 cache_key=cache_key) 49 co.save() # update modified timestamp 36 50 return value 37 51 … … 43 57 effect. 44 58 45 You can easily replace the default template cache, just change the load46 statement from ``{% load cache %}`` to ``{% load cache_smart %}``.47 48 59 Usage:: 49 60 50 {% load cache_smart %}51 61 {% cache [expire_time] [fragment_name] %} 52 62 .. some expensive processing .. … … 55 65 This tag also supports varying by a list of arguments:: 56 66 57 {% load cache_smart %}58 67 {% cache [expire_time] [fragment_name] [var1] [var2] .. %} 59 68 .. some expensive processing .. … … 61 70 62 71 Each unique set of arguments will result in a unique cache entry. 72 73 To enable automatic cache invalidation, a model object can be passed as 74 a final optional parameter. Whenever the object is saved, this template 75 fragment will be regenerated:: 76 77 {% cache [expire_time] [fragment_name] [var1] for [obj] %} 78 .. some expensive processing .. 79 {% endcache %} 80 81 See the 'cache_dup' template tag to enable automatic cache invalidation 82 based on more than a single object. 63 83 """ 64 84 nodelist = parser.parse(('endcache', )) … … 74 94 u"First argument to '%r' must be an integer (got '%s')." % 75 95 (tokens[0], tokens[1])) 96 if (tokens[-2] == 'for'): 97 return SmartCacheNode(nodelist, expire_time, tokens[2], tokens[3:-2], tokens[-1]) 76 98 return SmartCacheNode(nodelist, expire_time, tokens[2], tokens[3:]) 77 99 78 100 register.tag('cache', do_smart_cache) 101 102 class CacheDependencyNode(Node): 103 def __init__(self, deps): 104 self.deps = deps 105 106 def render(self, context): 107 if 'cache_key' in context: 108 for dep in self.deps: 109 obj = resolve_variable(dep, context) 110 co, created = CachedObject.objects.get_or_create(url=context['request'].get_full_path(), 111 content_type=ContentType.objects.get_for_model(obj), 112 object_id=obj.pk, 113 cache_key=context['cache_key']) 114 co.save() # update modified timestamp 115 return '' 116 117 def do_cache_dependency(parser, token): 118 """ 119 When used inside a {% cache %} template tag block, this template tag will 120 cause the entire cache block to be regenerated whenever any of the passed 121 parameters change:: 122 123 {% cache_dup [var1] %} 124 {% cache_dup [var1] [var2] %} 125 126 Example usage:: 127 128 {% cache .. for object_a %} 129 .. expensive processing .. 130 {% cache_dup object_b %} 131 .. more processing .. 132 {% cache_dup object_c object _d %} 133 {% endcache %} 134 135 In this example, when the model objects referred to by 'object_a', 136 'object_b', 'object_c', or 'object_d' are saved, this template fragment 137 will be automatically invalidated and regenerated. 138 139 There is no limit to the number of times 'cache_dup' can be called within 140 a single 'cache' block. 141 """ 142 bits = token.split_contents() 143 if len(bits) > 1: 144 return CacheDependencyNode(bits[1:]) 145 return '' 146 register.tag('cache_dep', do_cache_dependency)