API v4: change book representation when reading bookmarks to full.
authorRadek Czajka <rczajka@rczajka.pl>
Tue, 2 Jun 2026 08:47:35 +0000 (10:47 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Tue, 2 Jun 2026 08:47:35 +0000 (10:47 +0200)
src/api/urls.py
src/bookmarks/api/views.py
src/social/api/serializers.py
src/social/api/views.py

index 867e1fd..1597811 100644 (file)
@@ -34,6 +34,7 @@ urlpatterns1 = [
 urlpatterns = [
     path('2/', include((urlpatterns1, 'api'), namespace="v2")),
     path('3/', include((urlpatterns1, 'api'), namespace="v3")),
+    path('4/', include((urlpatterns1, 'api'), namespace="v4")),
     path('1/<path:path>', views.Unsupported.as_view()),
 
     path('oauth/request_token/', csrf_exempt(views.OAuth1RequestTokenView.as_view())),
index b500a66..82502e9 100644 (file)
@@ -13,9 +13,11 @@ from rest_framework.generics import ListAPIView, ListCreateAPIView, RetrieveUpda
 from rest_framework import serializers
 from rest_framework.permissions import SAFE_METHODS, IsAuthenticated, IsAuthenticatedOrReadOnly
 from api.fields import AbsoluteURLField
+from catalogue.api.serializers import BookSerializer2
 
 
-class BookmarkSerializer(serializers.ModelSerializer):
+class BookmarkSerializerV3(serializers.ModelSerializer):
+    """Replaced in API v4."""
     book = serializers.SlugRelatedField(
         queryset=catalogue.models.Book.objects.all(), slug_field='slug',
         required=False
@@ -30,12 +32,33 @@ class BookmarkSerializer(serializers.ModelSerializer):
         read_only_fields = ['uuid', 'mode']
 
 
+class BookmarkSerializer(serializers.ModelSerializer):
+    book_slug = serializers.SlugRelatedField(
+        queryset=catalogue.models.Book.objects.all(), slug_field='slug',
+        write_only=True,
+        required=False
+    )
+    book = BookSerializer2(read_only=True)
+    href = AbsoluteURLField(view_name='api_bookmark', view_args=['uuid'])
+    timestamp = serializers.IntegerField(required=False)
+    location = serializers.CharField(required=False)
+    
+    class Meta:
+        model = models.Bookmark
+        fields = ['book_slug', 'book', 'anchor', 'audio_timestamp', 'mode', 'note', 'href', 'uuid', 'location', 'timestamp', 'deleted']
+        read_only_fields = ['uuid', 'mode']
+
 
 @never_cache
 class BookmarksView(ListCreateAPIView):
     permission_classes = [IsAuthenticated]
-    serializer_class = BookmarkSerializer
 
+    def get_serializer_class(self):
+        if self.request.version < 'v4':
+            return BookmarkSerializerV3
+        else:
+            return BookmarkSerializer
+    
     def get_queryset(self):
         return self.request.user.bookmark_set.all()
 
@@ -46,9 +69,14 @@ class BookmarksView(ListCreateAPIView):
 @never_cache
 class BookBookmarksView(ListAPIView):
     permission_classes = [IsAuthenticated]
-    serializer_class = BookmarkSerializer
     pagination_class = None
 
+    def get_serializer_class(self):
+        if self.request.version < 'v4':
+            return BookmarkSerializerV3
+        else:
+            return BookmarkSerializer
+    
     def get_queryset(self):
         return self.request.user.bookmark_set.filter(book__slug=self.kwargs['book'])
 
@@ -56,9 +84,14 @@ class BookBookmarksView(ListAPIView):
 @never_cache
 class BookmarkView(RetrieveUpdateDestroyAPIView):
     permission_classes = [IsAuthenticatedOrReadOnly]
-    serializer_class = BookmarkSerializer
     lookup_field = 'uuid'
 
+    def get_serializer_class(self):
+        if self.request.version < 'v4':
+            return BookmarkSerializerV3
+        else:
+            return BookmarkSerializer
+    
     def get_queryset(self):
         if self.request.method in SAFE_METHODS:
             q = Q(deleted=False)
index cbaa3bb..155c0e6 100644 (file)
@@ -130,6 +130,27 @@ class UserListItemSerializer(serializers.ModelSerializer):
         }
 
 
+class UserListItemReadSerializerV3(UserListItemSerializer):
+    book = catalogue.api.serializers.BookSerializer2()
+    bookmark = bookmarks.api.views.BookmarkSerializerV3()
+    class Meta:
+        model = models.UserListItem
+        fields = [
+            'client_id',
+            'uuid',
+            'order',
+            'list_slug',
+            'timestamp',
+            'favorites',
+            'deleted',
+
+            'book',
+            'fragment',
+            'quote',
+            'bookmark',
+            'note',
+        ]
+
 class UserListItemReadSerializer(UserListItemSerializer):
     book = catalogue.api.serializers.BookSerializer2()
     bookmark = bookmarks.api.views.BookmarkSerializer()
index 2a57ad3..35af9a8 100644 (file)
@@ -194,7 +194,10 @@ class ListItemListViewV3(ListCreateAPIView):
 
     def get_serializer_class(self):
         if self.request.method == 'GET':
-            return serializers.UserListItemReadSerializer
+            if self.request.version < 'v4':
+                return serializers.UserListItemReadSerializerV3
+            else:
+                return serializers.UserListItemReadSerializer
         else:
             return serializers.UserListItemSerializer
     
@@ -215,7 +218,12 @@ class ListItemListViewV3(ListCreateAPIView):
 @never_cache
 class ListItemsForBook(ListAPIView):
     permission_classes = [IsAuthenticated]
-    serializer_class = serializers.UserListItemReadSerializer
+
+    def get_serializer_class(self):
+        if self.request.version < 'v4':
+            return serializers.UserListItemReadSerializerV3
+        else:
+            return serializers.UserListItemReadSerializer
 
     def get_queryset(self):
         book = get_object_or_404(catalogue.models.Book, slug=self.kwargs['book'])
@@ -250,7 +258,10 @@ class ListItemViewV3(RetrieveUpdateDestroyAPIView):
 
     def get_serializer_class(self):
         if self.request.method == 'GET':
-            return serializers.UserListItemReadSerializer
+            if self.request.version < 'v4':
+                return serializers.UserListItemReadSerializerV3
+            else:
+                return serializers.UserListItemReadSerializer
         else:
             return serializers.UserListItemSerializer