This repository has been archived on 2026-07-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
cloud-reader/backend/books/serializers.py
T
Marko (Hermes Implementer) 84d8fed3f2 feat: full book management system with backend API, frontend UI, and spec docs
- Backend: Book model with reading progress, DRF ViewSet with full CRUD,
  search, sort, filter, pagination, mark-as-finished, stats endpoint
- Frontend: Library grid, BookCard, BookDetail, BookForm components with
  React 19 + TypeScript + Vite
- Tests: 29 passing tests covering models, API, serializers, permissions
- Spec: backend api-spec.md and frontend component-spec.md in docs/

Closes crisleo-hermes/cloud-reader#3
2026-05-26 04:35:13 +00:00

95 lines
3.0 KiB
Python

from __future__ import annotations
from rest_framework import serializers
from books.models import Book, ReadingStatus
class BookListSerializer(serializers.ModelSerializer):
"""Lightweight serializer for list views — avoids heavy field serialization."""
reading_progress = serializers.FloatField(read_only=True)
class Meta:
model = Book
fields = [
"id",
"title",
"author",
"genre",
"reading_status",
"reading_progress",
"cover_image_url",
"created_at",
"updated_at",
]
class BookDetailSerializer(serializers.ModelSerializer):
"""Full serializer for book detail views including all metadata."""
reading_progress = serializers.FloatField(read_only=True)
owner = serializers.ReadOnlyField(source="owner.username")
class Meta:
model = Book
fields = [
"id",
"title",
"author",
"genre",
"description",
"cover_image_url",
"isbn",
"total_pages",
"current_page",
"reading_status",
"reading_progress",
"owner",
"created_at",
"updated_at",
]
read_only_fields = ["owner", "created_at", "updated_at", "reading_progress"]
def validate_title(self, value: str) -> str:
"""Ensure title is not just whitespace."""
stripped = value.strip()
if not stripped:
msg = "Title cannot be empty."
raise serializers.ValidationError(msg)
return stripped
def validate_author(self, value: str) -> str:
"""Ensure author is not just whitespace."""
stripped = value.strip()
if not stripped:
msg = "Author cannot be empty."
raise serializers.ValidationError(msg)
return stripped
def validate(self, attrs: dict) -> dict:
"""Business rules — handles both full creates and partial updates."""
# Resolve effective values: use provided attrs, fall back to instance
current_total = getattr(self.instance, "total_pages", None)
current_current = getattr(self.instance, "current_page", None)
total_pages = attrs.get("total_pages", current_total) or 0
current_page = attrs.get("current_page", current_current) or 0
reading_status = attrs.get("reading_status", None)
if current_page > total_pages > 0:
msg = "Current page cannot exceed total pages."
raise serializers.ValidationError({"current_page": msg})
if reading_status == ReadingStatus.FINISHED:
if total_pages > 0:
attrs["current_page"] = total_pages
elif self.instance and self.instance.total_pages > 0:
attrs["current_page"] = self.instance.total_pages
return attrs
class BookWriteSerializer(BookDetailSerializer):
"""Alias for detail serializer — used for write operations with full validation."""
pass