Skip to content

Instantly share code, notes, and snippets.

@rod-dot-codes
Forked from mjumbewu/views.py
Last active August 29, 2015 14:20
Show Gist options
  • Save rod-dot-codes/dd8f68609ed79bb8d460 to your computer and use it in GitHub Desktop.
Save rod-dot-codes/dd8f68609ed79bb8d460 to your computer and use it in GitHub Desktop.
from rest_framework.response import Response
import logging
import json
import re
import dill
from django.conf import settings
from django.core import cache as django_cache
from mock import patch
from rest_framework.permissions import SAFE_METHODS
logger = logging.getLogger(__name__)
class CachedResourceMixin (object):
@property
def cache_prefix(self):
return self.request.path
def get_cache_prefix(self):
return self.cache_prefix
def get_cache_metakey(self):
prefix = self.cache_prefix
return prefix + '_keys'
@csrf_exempt
def dispatch(self, request, *args, **kwargs):
# Only do the cache for GET, OPTIONS, or HEAD method.
if request.method.upper() not in SAFE_METHODS:
return super(CachedResourceMixin, self).dispatch(request, *args, **kwargs)
self.request = request
key = self.get_cache_key(request, *args, **kwargs)
response_data = None
if 'invalidate' not in self.request.GET:
# Check whether the response data is in the cache.
response_data = django_cache.cache.get(key) or None
# Also check whether the request cache key is managed in the cache.
# This is important, because if it's not managed, then we'll never
# know when to invalidate it. If it's not managed we should just
# assume that it's invalid.
metakey = self.get_cache_metakey()
keyset = django_cache.cache.get(metakey) or set()
else:
#INVALIDATE
keyset = []
#We need to be a bit more through and delete all the keys that include the id.
logger.info('Cache key is {0}'.format(key))
#The keys we need to get rid of are
logger.info('Cache keys are {}'.format(django_cache.cache.get(self.cache_prefix + '_keys')))
try:
if django_cache.cache.get(self.cache_prefix + '_keys') is not None:
for key_value in django_cache.cache.get(self.cache_prefix + '_keys'):
logger.info('{}'.format(key_value))
django_cache.cache.delete(key_value)
django_cache.cache.delete(self.cache_prefix + '_keys')
except:
pass
if (response_data is not None) and (key in keyset):
cached_response = self.respond_from_cache(response_data)
handler_name = request.method.lower()
def cached_handler(*args, **kwargs):
return cached_response
# Patch the HTTP method
with patch.object(self, handler_name, new=cached_handler):
response = super(CachedResourceMixin, self).dispatch(request, *args, **kwargs)
else:
response = super(CachedResourceMixin, self).dispatch(request, *args, **kwargs)
# Only cache on OK resposne
if response.status_code == 200:
self.cache_response(key, response)
# Disable client-side caching. Cause IE wrongly assumes when it should
# cache.
response['Cache-Control'] = 'no-cache'
response['cached'] = 'Cached'
return response
def get_cache_key(self, request, *args, **kwargs):
querystring = request.META.get('QUERY_STRING', '')
# TODO: Eliminate the jQuery cache busting parameter for now. Get
# rid of this after the old API has been deprecated.
cache_buster_pattern = re.compile(r'&?_=\d+')
querystring = re.sub(cache_buster_pattern, '', querystring)
return ':'.join([self.cache_prefix, querystring])
def respond_from_cache(self, cached_data):
# Given some cached data, construct a response.
content, status, headers = dill.loads(cached_data)
content = dill.loads(content)
status = dill.loads(status)
headers = dill.loads(headers)
response = Response(content, status=status, headers=dict(headers))
return response
def cache_response(self, key, response):
try:
data = dill.dumps(response.data)
status = dill.dumps(response.status_code)
headers = dill.dumps(dict(response.items()))
# Cache enough info to recreate the response.
django_cache.cache.set(key, dill.dumps((data, status, headers)), settings.API_CACHE_TIMEOUT)
# Also, add the key to the set of pages cached from this view.
meta_key = self.cache_prefix + '_keys'
keys = django_cache.cache.get(meta_key) or set()
keys.add(key)
django_cache.cache.set(meta_key, keys, settings.API_CACHE_TIMEOUT)
return response
except Exception as e:
print('Exception occured at Cache:{0}'.format(e))
logger.error('There was a cache error: {0}'.format(e), exc_info=True, extra={
# Optionally pass a request and we'll grab any information we can
'request': request,
})
return response
class MyAPIView (CachedResourceMixin, generics.RetrieveUpdateDestroyAPIView):
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment