django1/django/views/decorators/cache.py

65 lines
2.8 KiB
Python

from django.core.cache import cache
from django.utils.httpwrappers import HttpResponseNotModified
import cStringIO, datetime, gzip, md5
# From http://www.xhaus.com/alan/python/httpcomp.html#gzip
# Used with permission.
def compress_string(s):
zbuf = cStringIO.StringIO()
zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
zfile.write(s)
zfile.close()
return zbuf.getvalue()
def cache_page(view_func, cache_timeout, key_prefix=''):
"""
Decorator for views that tries getting the page from the cache and
populates the cache if the page isn't in the cache yet. Also takes care
of ETags and gzips the page if the client supports it.
The cache is keyed off of the page's URL plus the optional key_prefix
variable. Use key_prefix if your Django setup has multiple sites that
use cache; otherwise the cache for one site would affect the other. A good
example of key_prefix is to use sites.get_current().domain, because that's
unique across all CMS instances on a particular server.
"""
def _check_cache(request, *args, **kwargs):
try:
accept_encoding = request.META['HTTP_ACCEPT_ENCODING']
except KeyError:
accept_encoding = ''
accepts_gzip = 'gzip' in accept_encoding
cache_key = 'views.decorators.cache.cache_page.%s.%s.%s' % (key_prefix, request.path, accepts_gzip)
response = cache.get(cache_key, None)
if response is None:
response = view_func(request, *args, **kwargs)
content = response.get_content_as_string('utf-8')
if accepts_gzip:
content = compress_string(content)
response.content = content
response['Content-Encoding'] = 'gzip'
response['ETag'] = md5.new(content).hexdigest()
response['Content-Length'] = '%d' % len(content)
response['Last-Modified'] = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
cache.set(cache_key, response, cache_timeout)
else:
# Logic is from http://simon.incutio.com/archive/2003/04/23/conditionalGet
try:
if_none_match = request.META['HTTP_IF_NONE_MATCH']
except KeyError:
if_none_match = None
try:
if_modified_since = request.META['HTTP_IF_MODIFIED_SINCE']
except KeyError:
if_modified_since = None
if if_none_match is None and if_modified_since is None:
pass
elif if_none_match is not None and response['ETag'] != if_none_match:
pass
elif if_modified_since is not None and response['Last-Modified'] != if_modified_since:
pass
else:
return HttpResponseNotModified()
return response
return _check_cache