Implement: Refactor: Consolidate Duplicate Backend and Frontend Implementations (#11)

Reviewed and merged by Reid (Hermes Reviewer)

Co-authored-by: crisleo-hermes <hermes@codescripters.org>
Co-committed-by: crisleo-hermes <hermes@codescripters.org>
This commit was merged in pull request #11.
This commit is contained in:
2026-05-26 03:54:25 +00:00
committed by reid
parent 3b5b301e42
commit f84a593c5d
56 changed files with 999 additions and 2454 deletions
+91 -12
View File
@@ -1,24 +1,103 @@
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import IsAuthenticated
from typing import Any
from apps.books.models import Book
from apps.books.serializers import BookListSerializer, BookSerializer
from django.db.models import QuerySet, Q
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import parsers, permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from apps.books.models import Book, EBook, ReadingProgress, ReadingSettings
from apps.books.serializers import (
BookDetailSerializer, BookListSerializer, BookSerializer,
EBookDetailSerializer, EBookListSerializer, EBookUploadSerializer,
ReadingProgressSerializer, ReadingSettingsSerializer,
)
class BookViewSet(viewsets.ModelViewSet):
"""CRUD for books."""
queryset = Book.objects.all()
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ["author"]
search_fields = ["title", "author"]
ordering_fields = ["title", "author", "created_at"]
filterset_fields = ["author", "genre", "reading_status"]
search_fields = ["title", "author", "genre"]
ordering_fields = ["title", "author", "genre", "created_at"]
ordering = ["title"]
def get_serializer_class(self):
if self.action == "retrieve":
return BookDetailSerializer
if self.action == "list":
return BookListSerializer
return BookSerializer
return BookSerializer
def get_queryset(self) -> QuerySet[Book]:
qs = super().get_queryset()
query = self.request.query_params.get("q", "").strip()
if query:
qs = qs.filter(Q(title__icontains=query) | Q(author__icontains=query) | Q(genre__icontains=query))
return qs
@action(detail=False, methods=["get"], permission_classes=[AllowAny])
def genres(self, request: Request) -> Response:
genre_list = Book.objects.values_list("genre", flat=True).distinct().order_by("genre")
return Response([g for g in genre_list if g])
@action(detail=False, methods=["get"], permission_classes=[AllowAny])
def authors(self, request: Request) -> Response:
author_list = Book.objects.values_list("author", flat=True).distinct().order_by("author")
return Response([a for a in author_list if a])
class IsEBookOwner(permissions.BasePermission):
def has_object_permission(self, request: Request, view: object, obj: EBook) -> bool:
return obj.user == request.user
class EBookViewSet(viewsets.ModelViewSet):
parser_classes = [parsers.MultiPartParser, parsers.FormParser, parsers.JSONParser]
permission_classes = [IsAuthenticated, IsEBookOwner]
def get_serializer_class(self):
if self.action == "create":
return EBookUploadSerializer
if self.action == "list":
return EBookListSerializer
return EBookDetailSerializer
def get_queryset(self):
return EBook.objects.filter(user=self.request.user).select_related("reading_progress", "user")
@action(detail=True, methods=["get", "patch"])
def progress(self, request: Request, pk: int | None = None) -> Response:
ebook = self.get_object()
progress_obj, _created = ReadingProgress.objects.get_or_create(user=request.user, ebook=ebook)
if request.method == "GET":
serializer = ReadingProgressSerializer(progress_obj)
return Response(serializer.data)
serializer = ReadingProgressSerializer(progress_obj, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
class ReadingSettingsViewSet(viewsets.GenericViewSet):
permission_classes = [IsAuthenticated]
serializer_class = ReadingSettingsSerializer
def get_queryset(self):
return ReadingSettings.objects.filter(user=self.request.user)
def list(self, request: Request) -> Response:
settings_obj, _created = ReadingSettings.objects.get_or_create(user=request.user)
serializer = self.get_serializer(settings_obj)
return Response(serializer.data)
def partial_update(self, request: Request) -> Response:
settings_obj, _created = ReadingSettings.objects.get_or_create(user=request.user)
serializer = self.get_serializer(settings_obj, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)