Created
August 4, 2011 21:57
-
-
Save ewr/1126400 to your computer and use it in GitHub Desktop.
Object-Aware Cache
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.core.cache import cache | |
DEFAULT_TIMEOUT = 0 | |
SET_PREFIX = "obj:" | |
FSET_PREFIX = "sobj:" | |
def set(key,val,timeout = DEFAULT_TIMEOUT,objects = []): | |
""" | |
Unlike the normal django / redis cache, ContentCache takes a collection | |
of objects that this cache depends on. This allows the cache to be | |
expired automatically when expire_obj() is called for one of those | |
objects. ContentBase models should automatically expire their caches | |
on save. | |
objects array can include stringified keys or objects that support a | |
obj_key() method (such as ContentBase models). | |
from contentbase import cache | |
cache.set(key,val,0,['news/story:21563',segmentObject]) | |
""" | |
# first, set the object | |
cache.set(key,val,timeout) | |
# Make the full key | |
fullkey = cache.make_key(key) | |
# expire this key from existing sets | |
fset = cache._client.smembers( "%s%s"%(FSET_PREFIX,fullkey) ) | |
if fset: | |
for skey in fset: | |
cache._client.srem(skey,fullkey) | |
# now add this key to each object's set | |
keys = [] | |
for obj in objects: | |
if obj == None: | |
next | |
elif hasattr(obj,'obj_key'): | |
cache._client.sadd(SET_PREFIX+obj.obj_key(),fullkey) | |
keys.append(SET_PREFIX+obj.obj_key()) | |
else: | |
cache._client.sadd(SET_PREFIX+obj,fullkey) | |
keys.append(SET_PREFIX+obj) | |
# create our forward mapping | |
cache._client.delete("%s%s"%(FSET_PREFIX,fullkey)) | |
cache._client.sadd("%s%s"%(FSET_PREFIX,fullkey),keys) | |
#---------- | |
def get(key, default=None, version=None): | |
return cache.get(key,default,version) | |
#---------- | |
def expire_obj(obj): | |
""" | |
Expire all caches that depend on a given object. | |
from contentbase import cache | |
cache.expire_obj(obj) | |
obj can be a string or an object that supports a obj_key() method. | |
""" | |
key = obj | |
if hasattr(obj,'obj_key'): | |
key = obj.obj_key() | |
mem = cache._client.smembers( SET_PREFIX+key ) | |
if mem: | |
cache._client.delete(*mem) | |
#---------- | |
def expire_from_signal(sender, **kwargs): | |
expire_obj(kwargs['instance']) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.template import Library, Node, TemplateSyntaxError, Variable, VariableDoesNotExist | |
from django.template import resolve_variable | |
from mercer.contentbase import cache | |
from django.utils.encoding import force_unicode | |
from django.utils.http import urlquote | |
register = Library() | |
class CacheNode(Node): | |
def __init__(self, nodelist, expire_time_var, fragment_name, vary_on): | |
self.nodelist = nodelist | |
self.expire_time_var = Variable(expire_time_var) | |
self.fragment_name = fragment_name | |
self.vary_on = vary_on | |
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) | |
# run nodelist looking for content objects | |
context.REGISTERED_OBJECTS = [] | |
# Build a unicode key for this fragment and all vary-on's. | |
cache_key = self.fragment_name + ':'.join([urlquote(resolve_variable(var, context)) for var in self.vary_on]) | |
value = cache.get(cache_key) | |
if value is None: | |
value = self.nodelist.render(context) | |
cache.set(cache_key, value, expire_time, context.REGISTERED_OBJECTS) | |
return value | |
#---------- | |
class RegisterNode(Node): | |
def __init__(self, *args): | |
assert len(args) | |
self.args = list(args) | |
def render(self, context): | |
obj = resolve_variable(self.args[0],context) | |
context.REGISTERED_OBJECTS.append(obj) | |
return '' | |
#---------- | |
def do_cache(parser, token): | |
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]) | |
return CacheNode(nodelist, tokens[1], tokens[2], tokens[3:]) | |
#---------- | |
def register_content(parser,token): | |
""" | |
Registers a ContentBase object to the current cache. | |
Usage: | |
{% register_content <content obj> [<scheme>] %} | |
""" | |
return RegisterNode(*token.contents.split()[1:]) | |
register.tag('cache_content', do_cache) | |
register.tag('register_content', register_content) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% cache_content 0 hsection:national %} | |
<h3>National News</h3> | |
<ul class="related-links"> | |
{% for item in national|slice:":3" %} | |
{% register_content item %} | |
<li><a class="news-link" href="{{ item.get_absolute_url }}">{{ item.headline }}</a> | |
{% endfor %} | |
</ul> | |
{% endcache %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment