Django and memcache: clear cache keys
Let's play Django with Memcached. As the great framework Django is, it's so easy to activate any kind of cache in your project. Memcached is one of the options, but you can also work with DatabaseCache, FileBasedCache, LocMemCache, MemcachedCache, DummyCache (a kind of non-cache very useful for devel/test enviroments) or - of course - your own CustomCache if you want.
Activating cache
It's too easy to activate the cache feature, it's enough to set the preferences in settings, install python-memcached in your enviroment (in case you will use MemcachedCache), and not much more to do. A couple of examples:
1. Basic FileBasedCache settings:
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
2. MemcachedCache settings:
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
3. Depending on the enviroment you can use MemcachedCache and DummyCache:
# settings.devel.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
# settings.production.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
Setting the places where cache will act
Now that we have our project with a configured kind of cache, we must to say where and when to activate it. There are multiple ways to do it (on the whole site, views, templates, urls...). I'm going to use... let's say urls. So, in our urls.py we have to set a time and activate the cache:
# urls.py
from django.views.decorators.cache import cache_page
ctime = (60 * 24) # A day
urlpatterns = patterns('',
url(r'^$',
cache_page(ctime)(BlogIndexView.as_view()),
{},
'blog-index'
),
...
)
A simple server reload will be enough to have cache running. We can see it on action with django-debug-toolbar, django-memcache-status or something like that.
Clean a specific key cache
And now the funniest part. For example, talking about a blog tool, when you write a new post (or editing older one) the software should be able to remove some cache keys, i.e. the blog-index one (because you have a new post) and the post-detail other (because you must be able to inmediately see the changes in the post you're editting).
Following this link I've created a cache.py with this content:
# cache.py
# -*- coding: utf-8 -*-
def expire_view_cache(
view_name,
args=[],
namespace=None,
key_prefix=None,
method="GET"):
"""
This function allows you to invalidate any view-level cache.
view_name: view function you wish to invalidate or it's named url pattern
args: any arguments passed to the view function
namepace: optioal, if an application namespace is needed
key prefix: for the @cache_page decorator for the function (if any)
http://stackoverflow.com/questions/2268417/expire-a-view-cache-in-django
added: method to request to get the key generating properly
"""
from django.core.urlresolvers import reverse
from django.http import HttpRequest
from django.utils.cache import get_cache_key
from django.core.cache import cache
from django.conf import settings
# create a fake request object
request = HttpRequest()
request.method = method
if settings.USE_I18N:
request.LANGUAGE_CODE = settings.LANGUAGE_CODE
# Loookup the request path:
if namespace:
view_name = namespace + ":" + view_name
request.path = reverse(view_name, args=args)
# get cache key, expire if the cached item exists:
key = get_cache_key(request, key_prefix=key_prefix)
if key:
if cache.get(key):
cache.set(key, None, 0)
return True
return False
And last step is to call the expire_view_cache function on model form save hook (admin.py in this case):
# admin.py
from cache import expire_view_cache
class PostAdminForm(admin.ModelAdmin):
...
def save_model(self, request, obj, form, change):
expire_view_cache("blog-index")
expire_view_cache("post-detail", [obj.slug])
And that's all, we are able to clean/purge/remove the cache when a new post is added or edited. As you can see in the code, cache is fun but you have to be careful to set it on the right way.