django cache invalidation

general = { about, articles, links, projects }     meta = { date-posted: 2007-06-11 }

I use the Django cache middleware on this site. When I used to make a change to a post, or create a new post, the change would not show up until the cache times out for the /blog/ page....until today.

I found a way to invalidate the cache (delete entries) upon the save of an element in my model. When I create a new page, or modify an existing page, the /blog/ index is automatically un-cached. The next hit to the /blog/ page regenerates the cache. yay!

Here is a snippet from my model. The non-relevant code is removed. See the magic..

from django.core.cache import cache

class Post(models.Model):
   ...<snip>....

   def save(self):
      self.html = markdown(self.content)
      cache_prefix = 'views.decorators.cache.cache_page.cwx.'
      cache_postfix = '.d41d8cd98f00b204e9800998ecf8427e'
      # delete post cache itself
      cache.delete('%s%s%s' % (cache_prefix,self.get_absolute_url(),cache_postfix))
      # delete /blog/ cache
      cache.delete('%s%s%s' % (cache_prefix,'/blog/',cache_postfix))
      cache.delete('%s%s%s' % (cache_prefix,'/rss/blog/',cache_postfix))
      cache.delete('%s%s%s' % (cache_prefix,'/atom/blog/',cache_postfix))
      super(Post, self).save()

   ....<snip>.....

It isn't very pretty, but it works. The cache_postfix, is an md5sum of the client request headers. Since the client request headers for the page in the cache framework, are "", the md5sum is always the same there. I just add the file prefix and then invalidate the pages I want.

If cache.delete is called to remove a page that isn't cached (the cache file doesn't exist), it silently does nothing. It doesn't hurt to call it again if the page hasn't been cached between edits.

I will refactor the code pretty soon, to generalize it more. I want to use the permalink Django helper to remove the hard coded url values, and use the routes to generate those on the fly. That way if I change the paths at some point in the future, I won't have to go back and edit this code. It will just keep working, which is a Good Thing (tm).

I looked for a cleaner way to get the cache_prefix, and the cache_postfix, but I haven't found one yet.