forked from Kamva-Academy/Kamva-Backend
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0b4ebce
commit 12ebed3
Showing
5 changed files
with
130 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |