Login

Scalable and invalidateble cache_page decorator

Author:
marinho
Posted:
May 6, 2009
Language:
Python
Version:
1.0
Score:
0 (after 0 ratings)

This cache_page decorator can be used in replacement to Django's django.views.decorators.cache.cache_page.

It resolves two problems:

  • invalidation (its cache key is not dependent of header nor request, then you can use model signals (or method 'put' in Google App Engine) to invalidate a URL or a list of them)
  • easier to scale (can be used at once memcached server by many differente servers)

Feel free to show me where it can have problems or limitations.

Updated and improved a lot

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from django.core.cache import cache
from django.template.defaultfilters import slugify
from django.http import HttpResponse
from django.conf import settings

CACHE_KEY_PREFIX = settings.CACHE_MIDDLEWARE_KEY_PREFIX + 'cache-page:'

def invalidate_cache(url, html5=None):
    """Does cache invalidation for a given URL"""
    cache.set(make_cache_key(url, html5), None)

def invalidate_all_cached_pages():
    """Invalidates all cached pages at once."""
    keys = [key for key in cache._cache.keys() if key.startswith(CACHE_KEY_PREFIX)]
    cache.set_many(dict([(key, None) for key in keys]))

def make_cache_key(url, html5=None):
    key = CACHE_KEY_PREFIX + slugify(url)

    if html5 == True:
        key = '.'.join([key, 'html5'])
    elif html5 == False:
        key = '.'.join([key, 'html4'])

    return key

def cache_page(arg=None): # 1 day as default
    """This decorator works in replacement to Django's decorator with same name, but
    it doesn't use Django's middleware system to make cache key, so, it uses its own
    logic to do it and make possible invalidate cache.
    
    This decorator shouldn't be used to views with user-based data."""

    default_exp = 60 * 30 # 30 minutes

    def _wrapper(func):
        def _inner(request, *args, **kwargs):
            key = make_cache_key(request.get_full_path(), getattr(request, 'supports_html5', None))
            content = cache.get(key, None)

            if not content:
                resp = func(request, *args, **kwargs)
                content = resp.content

                # Only stores in cache if is a regular HttpResponse returning text/html
                if resp.__class__.__name__ == 'HttpResponse' and\
                   getattr(resp, 'mimetype', 'text/html') == 'text/html':
                    cache.set(key, content, expiration)
                else:
                    return resp

            return HttpResponse(content)
        return _inner

    if callable(arg):
        expiration = default_exp
        return _wrapper(arg)
    else:
        expiration = arg or default_exp 
        return _wrapper

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

Please login first before commenting.