From 904c899b029815b0056476df9c693662a6c27905 Mon Sep 17 00:00:00 2001 From: mortezaei Date: Thu, 16 Apr 2026 17:30:10 +0330 Subject: [PATCH] Add API views for bulk adding and removing bookmarks, with corresponding URL routes. --- apps/bookmark/urls.py | 4 +- apps/bookmark/views/bookmark.py | 132 ++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/apps/bookmark/urls.py b/apps/bookmark/urls.py index 4ee0b77..c45db51 100644 --- a/apps/bookmark/urls.py +++ b/apps/bookmark/urls.py @@ -1,12 +1,14 @@ from django.urls import path -from .views import AddBookmarkView, RemoveBookmarkView, BookmarkStatusView, AddRateView, RemoveRateView, RateStatusView, AverageRateView +from .views import AddBookmarkView, RemoveBookmarkView, BookmarkStatusView, AddBookmarkListView, RemoveBookmarkListView, AddRateView, RemoveRateView, RateStatusView, AverageRateView app_name = 'bookmark' urlpatterns = [ # Bookmark URLs path('add/', AddBookmarkView.as_view(), name='add_bookmark'), + path('add-list/', AddBookmarkListView.as_view(), name='add_bookmark_list'), path('remove/', RemoveBookmarkView.as_view(), name='remove_bookmark'), + path('remove-list/', RemoveBookmarkListView.as_view(), name='remove_bookmark_list'), path('status/', BookmarkStatusView.as_view(), name='bookmark_status'), # Rate URLs diff --git a/apps/bookmark/views/bookmark.py b/apps/bookmark/views/bookmark.py index beefd23..946ff67 100644 --- a/apps/bookmark/views/bookmark.py +++ b/apps/bookmark/views/bookmark.py @@ -130,6 +130,138 @@ class RemoveBookmarkView(DestroyAPIView): return Response(status=status.HTTP_204_NO_CONTENT) +class AddBookmarkListView(APIView): + """ + Add a list of bookmarks in a single API call. + Each item can be added or reactivated. + """ + permission_classes = [IsAuthenticated] + authentication_classes = [TokenAuthentication] + + @swagger_auto_schema( + operation_description="Add multiple bookmarks in one request. Each item should have 'service' and 'content_id'.", + tags=["Dobodbi - Bookmarks"], + request_body=openapi.Schema( + type=openapi.TYPE_ARRAY, + items=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'service': openapi.Schema(type=openapi.TYPE_STRING, description='Service name'), + 'content_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='Content ID'), + }, + required=['service', 'content_id'] + ) + ), + responses={ + 200: "Bookmarks processed successfully.", + 400: "Invalid input." + } + ) + def post(self, request, *args, **kwargs): + items = request.data + if not isinstance(items, list): + return Response({'error': 'Expected a list of bookmark items.'}, status=status.HTTP_400_BAD_REQUEST) + + results = [] + errors = [] + + for index, item in enumerate(items): + service = item.get('service') + content_id = item.get('content_id') + + if not service or not content_id: + errors.append({'index': index, 'error': 'Both service and content_id are required.'}) + continue + + bookmark = Bookmark.objects.filter( + user=request.user, + service=service, + content_id=content_id + ).first() + + if bookmark: + if not bookmark.status: + bookmark.status = True + bookmark.save() + results.append({'index': index, 'service': service, 'content_id': content_id, 'action': 'reactivated'}) + else: + serializer = BookmarkSerializer(data=item, context={'request': request}) + if serializer.is_valid(): + serializer.save(user=request.user) + results.append({'index': index, 'service': service, 'content_id': content_id, 'action': 'created'}) + else: + errors.append({'index': index, 'error': serializer.errors}) + + response_data = {'results': results} + if errors: + response_data['errors'] = errors + + return Response(response_data, status=status.HTTP_200_OK) + + +class RemoveBookmarkListView(APIView): + """ + Deactivate a list of bookmarks in a single API call. + """ + permission_classes = [IsAuthenticated] + authentication_classes = [TokenAuthentication] + + @swagger_auto_schema( + operation_description="Remove (deactivate) multiple bookmarks in one request. Each item should have 'service' and 'content_id'.", + tags=["Dobodbi - Bookmarks"], + request_body=openapi.Schema( + type=openapi.TYPE_ARRAY, + items=openapi.Schema( + type=openapi.TYPE_OBJECT, + properties={ + 'service': openapi.Schema(type=openapi.TYPE_STRING, description='Service name'), + 'content_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='Content ID'), + }, + required=['service', 'content_id'] + ) + ), + responses={ + 200: "Bookmarks processed successfully.", + 400: "Invalid input." + } + ) + def post(self, request, *args, **kwargs): + items = request.data + if not isinstance(items, list): + return Response({'error': 'Expected a list of bookmark items.'}, status=status.HTTP_400_BAD_REQUEST) + + results = [] + errors = [] + + for index, item in enumerate(items): + service = item.get('service') + content_id = item.get('content_id') + + if not service or not content_id: + errors.append({'index': index, 'error': 'Both service and content_id are required.'}) + continue + + bookmark = Bookmark.objects.filter( + user=request.user, + service=service, + content_id=content_id, + status=True + ).first() + + if bookmark: + bookmark.status = False + bookmark.save() + results.append({'index': index, 'service': service, 'content_id': content_id, 'action': 'removed'}) + else: + errors.append({'index': index, 'service': service, 'content_id': content_id, 'error': 'Bookmark not found or already inactive.'}) + + response_data = {'results': results} + if errors: + response_data['errors'] = errors + + return Response(response_data, status=status.HTTP_200_OK) + + class BookmarkStatusView(APIView): """ Return the count of bookmarks for each service for the current user.