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

@@ -1,3 +1,4 @@
from django.core.cache import cache
from django.db import transaction
from django.db.models import F
from django.shortcuts import get_object_or_404
@@ -38,26 +39,34 @@ class GuestAwarePurchaseThrottle(ScopedRateThrottle):
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):
"""GET /api/subscription/plans — list available plans."""
@extend_schema(responses=PlansListResponseSerializer, tags=['Subscription (subscription)'])
def get(self, request):
plans = Plan.objects.all()
data = {
'plans': [
{
'id': str(p.id),
'title': p.title,
'price': p.price,
'currency': p.currency,
'popular': p.popular,
'discountPercent': p.discount_percent,
'durationDays': p.duration_days,
}
for p in plans
]
}
def build():
return {
'plans': [
{
'id': str(p.id),
'title': p.title,
'price': p.price,
'currency': p.currency,
'popular': p.popular,
'discountPercent': p.discount_percent,
'durationDays': p.duration_days,
}
for p in Plan.objects.all()
]
}
data = cache.get_or_set(PLANS_CACHE_KEY, build, PLANS_CACHE_TTL)
return Response(PlansListResponseSerializer(data).data)