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