from typing import Any from fastapi.encoders import jsonable_encoder from sqlalchemy.orm import Session, joinedload from app.crud.base import CRUDBase from app.models.manga import Manga, MangaGenre from app.schemas.manga import MangaCreate, MangaUpdate class CRUDManga(CRUDBase[Manga, MangaCreate, MangaUpdate]): def get(self, db: Session, id: Any) -> Manga | None: """ Get a manga by ID with all relationships loaded. """ return ( db.query(Manga) .filter(Manga.id == id) .options( joinedload(Manga.author), joinedload(Manga.publisher), joinedload(Manga.genres).joinedload(MangaGenre.genre), ) .first() ) def get_by_isbn(self, db: Session, *, isbn: str) -> Manga | None: """ Get a manga by ISBN. """ return db.query(Manga).filter(Manga.isbn == isbn).first() def get_multi(self, db: Session, *, skip: int = 0, limit: int = 100) -> list[Manga]: """ Get multiple manga with all relationships loaded. """ return ( db.query(Manga) .options( joinedload(Manga.author), joinedload(Manga.publisher), joinedload(Manga.genres).joinedload(MangaGenre.genre), ) .offset(skip) .limit(limit) .all() ) def create(self, db: Session, *, obj_in: MangaCreate) -> Manga: """ Create a new manga with genre relationships. """ obj_in_data = jsonable_encoder(obj_in, exclude={"genre_ids"}) db_obj = Manga(**obj_in_data) db.add(db_obj) db.flush() # Flush to get the ID # Add genres if provided if obj_in.genre_ids: for genre_id in obj_in.genre_ids: manga_genre = MangaGenre(manga_id=db_obj.id, genre_id=genre_id) db.add(manga_genre) db.commit() db.refresh(db_obj) return db_obj def update(self, db: Session, *, db_obj: Manga, obj_in: MangaUpdate | dict[str, Any]) -> Manga: """ Update a manga with genre relationships. """ if isinstance(obj_in, dict): update_data = obj_in genre_ids = update_data.pop("genre_ids", None) else: update_data = obj_in.dict(exclude_unset=True) genre_ids = update_data.pop("genre_ids", None) if "genre_ids" in update_data else None # Update the manga object obj_data = jsonable_encoder(db_obj) for field in obj_data: if field in update_data: setattr(db_obj, field, update_data[field]) # Update genres if provided if genre_ids is not None: # Remove existing genres db.query(MangaGenre).filter(MangaGenre.manga_id == db_obj.id).delete() # Add new genres for genre_id in genre_ids: manga_genre = MangaGenre(manga_id=db_obj.id, genre_id=genre_id) db.add(manga_genre) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def search( self, db: Session, *, title: str | None = None, author_id: int | None = None, publisher_id: int | None = None, genre_id: int | None = None, in_stock: bool | None = None, skip: int = 0, limit: int = 100, ) -> list[Manga]: """ Search manga by various criteria. """ query = db.query(Manga) if title: query = query.filter(Manga.title.ilike(f"%{title}%")) if author_id: query = query.filter(Manga.author_id == author_id) if publisher_id: query = query.filter(Manga.publisher_id == publisher_id) if genre_id: query = query.join(Manga.genres).filter(MangaGenre.genre_id == genre_id) if in_stock is not None: query = query.filter(Manga.in_stock == in_stock) return ( query.options( joinedload(Manga.author), joinedload(Manga.publisher), joinedload(Manga.genres).joinedload(MangaGenre.genre), ) .offset(skip) .limit(limit) .all() ) manga = CRUDManga(Manga)