from typing import Any, Callable, Generic, Type, TypeVar from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.orm import Session ModelType = TypeVar("ModelType") CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) ResponseSchemaType = TypeVar("ResponseSchemaType", bound=BaseModel) class CRUDRouterFactory( Generic[ModelType, CreateSchemaType, UpdateSchemaType, ResponseSchemaType] ): """Factory to create standard CRUD routes""" def __init__( self, model: Type[ModelType], create_schema: Type[CreateSchemaType], update_schema: Type[UpdateSchemaType], response_schema: Type[ResponseSchemaType], db_dependency: Callable, auth_dependency: Callable, prefix: str, tags: list[str], id_field: str = "key", ): self.model = model self.create_schema = create_schema self.update_schema = update_schema self.response_schema = response_schema self.db_dependency = db_dependency self.auth_dependency = auth_dependency self.id_field = id_field self.router = APIRouter(prefix=prefix, tags=tags) self._register_routes() def _register_routes(self): """Register all CRUD routes""" @self.router.get("/", response_model=list[self.response_schema]) def list_items( skip: int = 0, limit: int = 100, db: Session = Depends(self.db_dependency), current_user: dict = Depends(self.auth_dependency), ): items = db.query(self.model).offset(skip).limit(limit).all() return items @self.router.get(f"/{{{self.id_field}}}", response_model=self.response_schema) def get_item( db: Session = Depends(self.db_dependency), current_user: dict = Depends(self.auth_dependency), **kwargs, ): item_id = kwargs.get(self.id_field) obj = ( db.query(self.model) .filter(getattr(self.model, self.id_field) == item_id) .first() ) if not obj: raise HTTPException(status_code=404, detail="Not found") return obj @self.router.post("/", response_model=self.response_schema) def create_item( data: Any, db: Session = Depends(self.db_dependency), current_user: dict = Depends(self.auth_dependency), ): obj = self.model(**data.dict()) db.add(obj) db.commit() db.refresh(obj) return obj @self.router.put(f"/{{{self.id_field}}}", response_model=self.response_schema) def update_item( data: Any, db: Session = Depends(self.db_dependency), current_user: dict = Depends(self.auth_dependency), **kwargs, ): item_id = kwargs.get(self.id_field) obj = ( db.query(self.model) .filter(getattr(self.model, self.id_field) == item_id) .first() ) if not obj: raise HTTPException(status_code=404, detail="Not found") for field, value in data.dict(exclude_unset=True).items(): setattr(obj, field, value) db.commit() db.refresh(obj) return obj @self.router.delete(f"/{{{self.id_field}}}", status_code=204) def delete_item( db: Session = Depends(self.db_dependency), current_user: dict = Depends(self.auth_dependency), **kwargs, ): item_id = kwargs.get(self.id_field) obj = ( db.query(self.model) .filter(getattr(self.model, self.id_field) == item_id) .first() ) if not obj: raise HTTPException(status_code=404, detail="Not found") db.delete(obj) db.commit() return None