From 698cca0aeb4ede006ea7c741a30742fb1c534e2c Mon Sep 17 00:00:00 2001 From: StepanovPlaton Date: Fri, 10 May 2024 12:15:18 +0400 Subject: [PATCH] Add requirements, cli commands. New project structure --- cli_commands.py | 14 ++++++++++++++ database/__init__.py | 3 ++- database/crud.py | 8 -------- database/crud/__init__.py | 1 + database/crud/games.py | 16 ++++++++++++++++ database/database.py | 19 +++++++++++++++++-- database/models.py | 12 +++++++----- database/schemas.py | 6 ++++-- main.py | 22 +++++++--------------- requirements.txt | 5 +++++ routes/__init__.py | 1 + routes/games.py | 20 ++++++++++++++++++++ 12 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 cli_commands.py delete mode 100644 database/crud.py create mode 100644 database/crud/__init__.py create mode 100644 database/crud/games.py create mode 100644 requirements.txt create mode 100644 routes/__init__.py create mode 100644 routes/games.py diff --git a/cli_commands.py b/cli_commands.py new file mode 100644 index 0000000..1a0b3a1 --- /dev/null +++ b/cli_commands.py @@ -0,0 +1,14 @@ + +from asyncio import run as aiorun +import typer + +import database + +cli = typer.Typer() + +@cli.command(name="create") +def create_database(): aiorun(database.create_all()) +@cli.command(name="drop") +def drop_database(): aiorun(database.drop_all()) +@cli.command(name="recreate") +def recreate_database(): aiorun(database.recreate_all()) diff --git a/database/__init__.py b/database/__init__.py index a75f677..6757f87 100644 --- a/database/__init__.py +++ b/database/__init__.py @@ -1,3 +1,4 @@ from .crud import * from .schemas import * -from .database import get_session, init_models +from .database import get_session, drop_all, create_all, recreate_all +from .crud import * \ No newline at end of file diff --git a/database/crud.py b/database/crud.py deleted file mode 100644 index 114d436..0000000 --- a/database/crud.py +++ /dev/null @@ -1,8 +0,0 @@ -from sqlalchemy.ext.asyncio import AsyncSession -from sqlalchemy.future import select - -from .models import * - - -async def get_user(db: AsyncSession, user_id: int): - return await db.get(User, user_id) diff --git a/database/crud/__init__.py b/database/crud/__init__.py new file mode 100644 index 0000000..97dd736 --- /dev/null +++ b/database/crud/__init__.py @@ -0,0 +1 @@ +from .games import * \ No newline at end of file diff --git a/database/crud/games.py b/database/crud/games.py new file mode 100644 index 0000000..1693ed5 --- /dev/null +++ b/database/crud/games.py @@ -0,0 +1,16 @@ +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.future import select + +from .. import models as mdl +from .. import schemas as sch +from ..database import add_transaction + +async def add_game(db: AsyncSession, game_info: sch.GameCreate, user_id: int): + game = mdl.Game(**game_info.model_dump(), owner_id=user_id) + return await add_transaction(db, game) + +async def get_games(db: AsyncSession): + return (await db.execute(select(mdl.Game))).scalars().all() + +async def get_game(db: AsyncSession, game_id: int): + return await db.get(mdl.Game, game_id) \ No newline at end of file diff --git a/database/database.py b/database/database.py index 31e1e93..12928f1 100644 --- a/database/database.py +++ b/database/database.py @@ -19,8 +19,23 @@ async def get_session() -> AsyncSession: # type: ignore async with async_session() as session: # type: ignore yield session - -async def init_models(): +async def drop_all(): async with engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) +async def create_all(): + async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) + +async def recreate_all(): + await drop_all() + await create_all() + +async def add_transaction[T](db: AsyncSession, entity: T) -> T: + try: + db.add(entity) + await db.commit() + await db.refresh(entity) + return entity + except Exception as ex: + await db.rollback() + raise ex \ No newline at end of file diff --git a/database/models.py b/database/models.py index 3f405d0..af6f4e0 100644 --- a/database/models.py +++ b/database/models.py @@ -1,5 +1,5 @@ -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String +from sqlalchemy import Column, ForeignKey, Integer, String from sqlalchemy.orm import relationship from .database import Base @@ -9,8 +9,10 @@ class Game(Base): __tablename__ = "games" id = Column(Integer, primary_key=True) - title = Column(String) + cover = Column(String) + title = Column(String, nullable=False) description = Column(String) + torrent_file = Column(String, nullable=False) language = Column(String) version = Column(String) download_size = Column(String) @@ -30,8 +32,8 @@ class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) - email = Column(String, unique=True) - name = Column(String) - hash_of_password = Column(String) + email = Column(String, nullable=False, unique=True) + name = Column(String, nullable=False) + hash_of_password = Column(String, nullable=False) games = relationship("Game", back_populates="owner") diff --git a/database/schemas.py b/database/schemas.py index 5f1f387..0222b00 100644 --- a/database/schemas.py +++ b/database/schemas.py @@ -2,8 +2,10 @@ from pydantic import BaseModel class GameBase(BaseModel): + cover: str | None = None title: str description: str | None = None + torrent_file: str language: str | None = None version: str | None = None download_size: str | None = None @@ -25,7 +27,7 @@ class Game(GameBase): owner_id: int class Config: - orm_mode = True + from_attributes = True class UserBase(BaseModel): @@ -42,4 +44,4 @@ class User(UserBase): games: list[Game] = [] class Config: - orm_mode = True + from_attributes = True diff --git a/main.py b/main.py index a58a6ad..9507378 100644 --- a/main.py +++ b/main.py @@ -1,22 +1,14 @@ -from typing import Union - from fastapi import Depends, FastAPI, HTTPException from sqlalchemy.ext.asyncio import AsyncSession +import typer -from database import * +import cli_commands +from routes import * app = FastAPI() +app.include_router(games_router) +cli = typer.Typer() +cli.add_typer(cli_commands.cli, name="database") -@app.on_event("startup") -async def startup_event(): - await init_models() - - -@app.get("/users/{user_id}", response_model=User) -async def read_user(user_id: int, db: AsyncSession = Depends(get_session)): - db_user = await get_user(db, user_id=user_id) - print(db_user) - if db_user is None: - raise HTTPException(status_code=404, detail="User not found") - return db_user +if(__name__ == "__main__"): cli() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a85bec0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.111.0 +pydantic==2.7.1 +SQLAlchemy==2.0.30 +aiosqlite==0.20.0 +typer==0.12.3 \ No newline at end of file diff --git a/routes/__init__.py b/routes/__init__.py new file mode 100644 index 0000000..45686f9 --- /dev/null +++ b/routes/__init__.py @@ -0,0 +1 @@ +from .games import router as games_router \ No newline at end of file diff --git a/routes/games.py b/routes/games.py new file mode 100644 index 0000000..815dcac --- /dev/null +++ b/routes/games.py @@ -0,0 +1,20 @@ +from fastapi import APIRouter, Depends, HTTPException + +from database import * + +router = APIRouter(prefix="/games", tags=["Games"]) + +@router.get("/", response_model=list[Game]) +async def get_games(db: AsyncSession = Depends(get_session)): + try: return await crud.get_games(db) + except Exception as ex: raise HTTPException(500) + +@router.get("/{game_id}", response_model=Game) +async def get_game(game_id: int, db: AsyncSession = Depends(get_session)): + return await crud.get_game(db, game_id) + +@router.post("/", response_model=Game) +async def add_game(game: GameCreate, user_id: int, db:AsyncSession = Depends(get_session)): + try: return await crud.add_game(db, game, user_id) + except Exception as ex: raise HTTPException(500) + \ No newline at end of file