cache the subscription plans list

subscription/views.py
  - PlansListView: served from cache (subscription:plans:v1, 1h TTL);
    plans change ~never and the list is read on every subscription screen.
subscription/receivers.py (new)
  - post_save/post_delete on Plan -> cache.delete(PLANS_CACHE_KEY).
subscription/apps.py
  - SubscriptionConfig.ready() imports receivers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 08:46:30 +03:30
parent d25ecab0b3
commit 40b4dd53a9
3 changed files with 41 additions and 15 deletions

View File

@@ -4,3 +4,6 @@ from django.apps import AppConfig
class SubscriptionConfig(AppConfig): class SubscriptionConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'subscription' name = 'subscription'
def ready(self):
from . import receivers # noqa: F401 (باطل‌کردنِ کشِ پلن‌ها)

14
subscription/receivers.py Normal file
View File

@@ -0,0 +1,14 @@
"""باطل‌کردنِ کشِ لیستِ پلن‌ها با هر تغییرِ Plan (از ادمین)."""
from django.core.cache import cache
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from .models import Plan
PLANS_CACHE_KEY = 'subscription:plans:v1' # هم‌تراز با subscription/views.py
@receiver(post_save, sender=Plan, dispatch_uid='subscription_invalidate_plans_cache_on_save')
@receiver(post_delete, sender=Plan, dispatch_uid='subscription_invalidate_plans_cache_on_delete')
def _invalidate_plans_cache(sender, **kwargs):
cache.delete(PLANS_CACHE_KEY)

View File

@@ -1,3 +1,4 @@
from django.core.cache import cache
from django.db import transaction from django.db import transaction
from django.db.models import F from django.db.models import F
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
@@ -38,13 +39,19 @@ class GuestAwarePurchaseThrottle(ScopedRateThrottle):
return self.cache_format % {'scope': self.scope, 'ident': ident} return self.cache_format % {'scope': self.scope, 'ident': ident}
# پلن‌ها تقریباً هیچ‌وقت عوض نمی‌شوند و روی صفحه‌ی اشتراک هر بار خوانده می‌شوند.
# با هر save/delete روی Plan کش باطل می‌شود (subscription/receivers.py).
PLANS_CACHE_KEY = 'subscription:plans:v1'
PLANS_CACHE_TTL = 3600
class PlansListView(APIView): class PlansListView(APIView):
"""GET /api/subscription/plans — list available plans.""" """GET /api/subscription/plans — list available plans."""
@extend_schema(responses=PlansListResponseSerializer, tags=['Subscription (subscription)']) @extend_schema(responses=PlansListResponseSerializer, tags=['Subscription (subscription)'])
def get(self, request): def get(self, request):
plans = Plan.objects.all() def build():
data = { return {
'plans': [ 'plans': [
{ {
'id': str(p.id), 'id': str(p.id),
@@ -55,9 +62,11 @@ class PlansListView(APIView):
'discountPercent': p.discount_percent, 'discountPercent': p.discount_percent,
'durationDays': p.duration_days, 'durationDays': p.duration_days,
} }
for p in plans for p in Plan.objects.all()
] ]
} }
data = cache.get_or_set(PLANS_CACHE_KEY, build, PLANS_CACHE_TTL)
return Response(PlansListResponseSerializer(data).data) return Response(PlansListResponseSerializer(data).data)