You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

69 lines
2.9 KiB

from functools import wraps
import logging
from django.core.cache import cache
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_headers
logger = logging.getLogger(__name__)
def invalidate_cache_patterns(*patterns):
"""
Safely invalidates Redis cache keys matching given glob patterns.
Falls back to cache.clear() if the cache backend does not support delete_pattern.
"""
try:
if hasattr(cache, 'delete_pattern'):
total_deleted = 0
for pattern in patterns:
deleted = cache.delete_pattern(pattern)
total_deleted += deleted or 0
logger.info(f"Cache cleared for patterns {patterns} (deleted {total_deleted} keys)")
return total_deleted
else:
cache.clear()
logger.info(f"Cache cleared (fallback clear for patterns: {patterns})")
return 1
except Exception as e:
logger.error(f"Cache invalidation failed for patterns {patterns}: {e}")
return 0
def smart_cached_view(timeout=60 * 60 * 2, key_prefix=''):
"""
Smart cache decorator for API views that:
1. Caches responses per Accept-Language header using Django's cache_page.
2. Bypasses cache when:
- Cache-Control / Pragma: no-cache headers are present (e.g., Pull-to-refresh from mobile / web).
- Query parameters: no_cache=true, refresh=true, is_bookmark=true, in_playlist=true are passed.
"""
def decorator(view_func):
cached_func = cache_page(timeout, key_prefix=key_prefix)(
vary_on_headers('Accept-Language')(view_func)
)
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# 1. Check HTTP headers for no-cache directives
cache_control = (request.META.get('HTTP_CACHE_CONTROL') or '').lower()
pragma = (request.META.get('HTTP_PRAGMA') or '').lower()
if 'no-cache' in cache_control or 'no-store' in cache_control or 'max-age=0' in cache_control or 'no-cache' in pragma:
return view_func(request, *args, **kwargs)
# 2. Check query params for explicit bypass
query_params = getattr(request, 'GET', {})
no_cache_param = (query_params.get('no_cache') or query_params.get('refresh') or '').lower()
if no_cache_param in ('1', 'true'):
return view_func(request, *args, **kwargs)
# 3. Check bookmark & playlist specific bypasses
is_bookmark = (query_params.get('is_bookmark') or query_params.get('is_bookmarked') or '').lower()
in_playlist = (query_params.get('in_playlist') or '').lower()
if is_bookmark in ('1', 'true') or in_playlist in ('1', 'true'):
return view_func(request, *args, **kwargs)
return cached_func(request, *args, **kwargs)
return _wrapped_view
return decorator