from contextlib import asynccontextmanager from fastapi import Depends, FastAPI, HTTPException, Query from sqlmodel import Session, select from models import ( Hero, HeroCreate, HeroRead, HeroReadWithTeam, HeroUpdate, Team, TeamCreate, TeamRead, TeamReadWithHeroes, create_db_and_tables, get_session, ) from security import hash_password @asynccontextmanager async def lifespan(app: FastAPI): create_db_and_tables() yield app = FastAPI(lifespan=lifespan) @app.post("/heroes/", response_model=HeroRead) def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): hashed_password = hash_password(hero.password) extra_data = {"hashed_password": hashed_password} db_hero = Hero.model_validate(hero, update=extra_data) session.add(db_hero) session.commit() session.refresh(db_hero) return db_hero @app.get("/heroes/", response_model=list[HeroRead]) def read_heroes( *, session: Session = Depends(get_session), offset: int = 0, limit: int = Query(default=100, le=100), ): heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() return heroes @app.get("/heroes/{hero_id}", response_model=HeroReadWithTeam) def read_hero(*, session: Session = Depends(get_session), hero_id: int): hero = session.get(Hero, hero_id) if not hero: return HTTPException(status_code=404, detail="Hero not found") return hero @app.patch("/heroes/{hero_id}", response_model=HeroRead) def update_hero( *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate ): db_hero = session.get(Hero, hero_id) if not db_hero: return HTTPException(status_code=404, detail="Hero not found") hero_data = hero.model_dump(exclude_unset=True) extra_data = {} if "password" in hero_data: password = hero_data["password"] hashed_password = hash_password(password) extra_data["hashed_password"] = hashed_password db_hero.sqlmodel_update(hero_data, update=extra_data) session.add(db_hero) session.commit() session.refresh(db_hero) return db_hero @app.delete("/heroes/{hero_id}") def delete_hero(*, session: Session = Depends(get_session), hero_id: int): hero = session.get(Hero, hero_id) if not hero: return HTTPException(status_code=404, detail="Hero not found") session.delete(hero) session.commit() return {"ok": True} @app.post("/teams/", response_model=TeamRead) def create_team(*, session: Session = Depends(get_session), team: TeamCreate): db_team = Team.model_validate(team) session.add(db_team) session.commit() session.refresh(db_team) return db_team @app.get("/teams/", response_model=list[TeamRead]) def read_teams( *, session: Session = Depends(get_session), offset: int = 0, limit: int = Query(default=100, le=100), ): teams = session.exec(select(Team).offset(offset).limit(limit)).all() return teams @app.get("/teams/{team_id}", response_model=TeamReadWithHeroes) def read_team(*, session: Session = Depends(get_session), team_id: int): team = session.get(Team, team_id) if not team: return HTTPException(status_code=404, detail="Team not found") return team @app.patch("/teams/{team_id}", response_model=TeamRead) def update_team( *, session: Session = Depends(get_session), team_id: int, team: TeamCreate ): db_team = session.get(Team, team_id) if not db_team: return HTTPException(status_code=404, detail="Team not found") team_data = team.model_dump(exclude_unset=True) db_team.sqlmodel_update(team_data) session.add(db_team) session.commit() session.refresh(db_team) return db_team @app.delete("/teams/{team_id}") def delete_team(*, session: Session = Depends(get_session), team_id: int): team = session.get(Team, team_id) if not team: return HTTPException(status_code=404, detail="Team not found") session.delete(team) session.commit() return {"ok": True}