accounts.0008 drops the columns and refuses to run while guest rows exist. Docs describe the current state; tests that only proved guest behaviour are gone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
73 lines
3.4 KiB
Python
73 lines
3.4 KiB
Python
from django.shortcuts import get_object_or_404
|
||
from drf_spectacular.utils import extend_schema
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from rest_framework.response import Response
|
||
from rest_framework.throttling import ScopedRateThrottle
|
||
from rest_framework.views import APIView
|
||
|
||
from .models import Notification
|
||
from .serializers import (
|
||
MarkAllReadResponseSerializer, MarkReadResponseSerializer, NotificationListResponseSerializer,
|
||
)
|
||
|
||
# سقفِ تعدادِ اعلانِ برگشتی؛ جلوی رشدِ نامحدودِ پاسخ برای کاربرِ قدیمی رو
|
||
# میگیره. صفحهبندیِ واقعی (cursor) وقتی فرانت infinite-scroll اضافه کنه.
|
||
NOTIFICATIONS_MAX = 100
|
||
|
||
|
||
class NotificationListView(APIView):
|
||
"""GET /api/notifications — فهرست اعلانهای کاربر (جدیدترین اول)"""
|
||
|
||
permission_classes = [IsAuthenticated]
|
||
throttle_scope = 'read'
|
||
throttle_classes = [ScopedRateThrottle]
|
||
|
||
@extend_schema(responses=NotificationListResponseSerializer, tags=['Notifications (notifications)'])
|
||
def get(self, request):
|
||
qs = Notification.objects.filter(user_id=request.user.id) # Meta.ordering: جدیدترین اول
|
||
unread_count = qs.filter(is_read=False).count()
|
||
|
||
data = {'items': list(qs[:NOTIFICATIONS_MAX]), 'unreadCount': unread_count}
|
||
return Response(NotificationListResponseSerializer(data).data)
|
||
|
||
|
||
class NotificationMarkReadView(APIView):
|
||
"""PATCH /api/notifications/{id}/read — علامتگذاری یک اعلان بهعنوان خواندهشده"""
|
||
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
# request=None لازم است: بدونِ آن spectacular نمیتواند بدنهی PATCH را حدس
|
||
# بزند و **کلِ ویو را از اسکیما کنار میگذارد** — یعنی این مسیر در قراردادی
|
||
# که فرانت از آن کلاینت میسازد اصلاً دیده نمیشود.
|
||
@extend_schema(request=None, responses=MarkReadResponseSerializer,
|
||
tags=['Notifications (notifications)'])
|
||
def patch(self, request, id):
|
||
notification = get_object_or_404(Notification, pk=id, user_id=request.user.id)
|
||
if not notification.is_read:
|
||
notification.is_read = True
|
||
notification.save(update_fields=['is_read'])
|
||
|
||
return Response(MarkReadResponseSerializer({'success': True}).data)
|
||
|
||
|
||
class NotificationMarkAllReadView(APIView):
|
||
"""
|
||
PATCH /api/notifications/read-all — همهی اعلانهای کاربر خواندهشده.
|
||
|
||
دکمهی «همه خوانده شد» پیش از این برای هر اعلان یک درخواستِ جدا میزد؛
|
||
حسابی با ۷۵ اعلان یعنی ۷۵ درخواستِ همزمان که سهمِ throttleِ read را
|
||
(همان که /home و بقیه مصرف میکنند) یکجا میخورد.
|
||
"""
|
||
|
||
permission_classes = [IsAuthenticated]
|
||
throttle_scope = 'read'
|
||
throttle_classes = [ScopedRateThrottle]
|
||
|
||
@extend_schema(request=None, responses=MarkAllReadResponseSerializer,
|
||
tags=['Notifications (notifications)'])
|
||
def patch(self, request):
|
||
updated = Notification.objects.filter(
|
||
user_id=request.user.id, is_read=False,
|
||
).update(is_read=True)
|
||
return Response(MarkAllReadResponseSerializer({'success': True, 'updated': updated}).data)
|