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
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
|