Skip to content

Commit

Permalink
fix: fix cache model view set
Browse files Browse the repository at this point in the history
  • Loading branch information
AmooHashem committed Aug 31, 2024
1 parent 0b4ebce commit 12ebed3
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 90 deletions.
4 changes: 2 additions & 2 deletions apps/fsm/views/fsm_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
from apps.fsm.serializers.widgets.mock_widget_serializer import MockWidgetSerializer
from apps.fsm.serializers.widgets.widget_polymorphic_serializer import WidgetPolymorphicSerializer
from apps.fsm.utils import get_player, get_receipt, get_a_player_from_team, _get_fsm_edges, register_user_in_program, transit_player_in_fsm
from utilities.cache_model_viewset import CacheModelViewSet
from utilities.cache_model_viewset.cache_model_viewset import CacheEnabledModelViewSet


class FSMViewSet(CacheModelViewSet):
class FSMViewSet(CacheEnabledModelViewSet):
permission_classes = [IsAuthenticated]
queryset = FSM.objects.filter(is_deleted=False)
ordering_fields = ['order_in_program']
Expand Down
4 changes: 2 additions & 2 deletions apps/fsm/views/program_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
from apps.accounts.utils import find_user_in_website
from apps.fsm.utils import add_admin_to_program
from errors.error_codes import serialize_error
from utilities.cache_model_viewset import CacheModelViewSet
from utilities.cache_model_viewset.cache_model_viewset import CacheEnabledModelViewSet
from utilities.safe_auth import SafeTokenAuthentication


class ProgramViewSet(CacheModelViewSet):
class ProgramViewSet(CacheEnabledModelViewSet):
queryset = Program.objects.filter(is_deleted=False)
serializer_class = ProgramSerializer
pagination_class = ProgramsPagination
Expand Down
86 changes: 0 additions & 86 deletions utilities/cache_model_viewset.py

This file was deleted.

56 changes: 56 additions & 0 deletions utilities/cache_model_viewset/cache_model_viewset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import Any
from rest_framework.viewsets import ModelViewSet
from django.utils.decorators import method_decorator

from utilities.cache_model_viewset.model_view_set_cache import ModelViewSetCache


class CacheEnabledModelViewSet(ModelViewSet):
add_no_cache_headers = True
cache = ModelViewSetCache()

def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)

@method_decorator(cache.decorator())
def list(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)

@method_decorator(cache.decorator())
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)

def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
self._invalidate_list_cache()
return response

def update(self, request, *args, **kwargs):
response = super().update(request, *args, **kwargs)
self.cache.invalidate_list_cache()
self._invalidate_object_cache(self.get_object())
return response

def partial_update(self, request, *args, **kwargs):
response = super().partial_update(request, *args, **kwargs)
self._invalidate_list_cache()
self._invalidate_object_cache(self.get_object())
return response

def destroy(self, request, *args, **kwargs):
obj = self.get_object() # Get the object before deletion
response = super().destroy(request, *args, **kwargs)
self.cache.invalidate_list_cache()
self.cache.invalidate_object_cache(obj)
return response

def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
if self.add_no_cache_headers:
self._add_no_cache_headers(response)
return response

def _add_no_cache_headers(self, response):
response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
response['Pragma'] = 'no-cache'
response['Expires'] = '0'
70 changes: 70 additions & 0 deletions utilities/cache_model_viewset/model_view_set_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from functools import wraps
from rest_framework.response import Response
from django.core.cache import cache


class ModelViewSetCache:
cache_timeout = 60 * 15 # 15 minutes
lookup_field = 'pk' # Default to 'pk', can be overridden in subclasses

def get_list_cache_key(self, key_prefix):
return f"{key_prefix}:list"

def get_object_cache_key(self, key_prefix, lookup_value):
return f"{key_prefix}:retrieve:{lookup_value}"

def invalidate_list_cache(self):
cache_key = self.get_list_cache_key(self.request)
cache.delete(cache_key)

def invalidate_object_cache(self, obj):
# Invalidate cache for both pk and slug if available
cache_key_pk = self.get_object_cache_key(obj.pk)
cache.delete(cache_key_pk)

if hasattr(obj, 'slug'):
cache_key_slug = self.get_object_cache_key(obj.slug)
cache.delete(cache_key_slug)

def decorator(self, timeout=cache_timeout, key_prefix=''):
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):

# Determine if it's a retrieve or list function
is_retrieve = view_func.__name__ == 'retrieve'
is_list = view_func.__name__ == 'list'

# Generate a unique cache key
if is_retrieve:
# Get pk or slug
pk = kwargs.get('pk')
slug = kwargs.get('slug')
if pk or slug:
key = self.get_object_cache_key(key_prefix, pk or slug)
else:
raise Exception('no pk or slug provided')
if is_list:
key = self.get_list_cache_key(key_prefix)

cached_response = cache.get(key)

if cached_response:
# If response is found in cache, return it with appropriate cache headers
response = Response(cached_response)
response['Cache-Control'] = f'max-age={timeout}, public'
return response

# Call the original view function
response = view_func(request, *args, **kwargs)

# Cache the response data if the response is an instance of Response
if isinstance(response, Response):
cache.set(key, response.data, timeout=timeout)

# Set cache headers in the response
response['Cache-Control'] = f'max-age={timeout}, public'

return response
return _wrapped_view
return decorator

0 comments on commit 12ebed3

Please sign in to comment.