diff --git a/subscription/apps.py b/subscription/apps.py index e14c1d4..72aa1f2 100644 --- a/subscription/apps.py +++ b/subscription/apps.py @@ -4,3 +4,6 @@ from django.apps import AppConfig class SubscriptionConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'subscription' + + def ready(self): + from . import receivers # noqa: F401 (باطل‌کردنِ کشِ پلن‌ها) diff --git a/subscription/receivers.py b/subscription/receivers.py new file mode 100644 index 0000000..5e40d04 --- /dev/null +++ b/subscription/receivers.py @@ -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) diff --git a/subscription/views.py b/subscription/views.py index ff716ec..92c8b58 100644 --- a/subscription/views.py +++ b/subscription/views.py @@ -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)