diff --git a/alembic.ini b/alembic.ini index 4f70ed2..44c9a46 100644 --- a/alembic.ini +++ b/alembic.ini @@ -60,7 +60,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = postgresql://demo_user:user_pass@db:5432/serviceman_db +sqlalchemy.url = postgresql://demo_user:user_pass@localhost:5432/serviceman_db [post_write_hooks] diff --git a/api/app.py b/api/app.py index d4e055d..574f82b 100644 --- a/api/app.py +++ b/api/app.py @@ -1,6 +1,6 @@ from fastapi import FastAPI -from api.router.user import router as user_router +from api.routers.user import router as user_router def create_app() -> FastAPI: diff --git a/api/config.py b/api/config.py index a1c457d..c987da5 100644 --- a/api/config.py +++ b/api/config.py @@ -3,7 +3,7 @@ import os import yaml # type: ignore from pydantic_settings import BaseSettings -with open(os.getenv("CONFIG_PATH", "")) as f: +with open(os.getenv("CONFIG_PATH", "./config/api_config.yml")) as f: config_data: dict = yaml.safe_load(f) if os.getenv("INDOCKER"): diff --git a/api/di.py b/api/di.py index 4beeb59..d461934 100644 --- a/api/di.py +++ b/api/di.py @@ -1,12 +1,13 @@ -from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine +from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker, + create_async_engine) from api.config import get_settings -from api.service.user import UserService +from api.services.user import UserService from api.uow.uow_base import UnitOfWork async_engine = create_async_engine( url=get_settings().get_db_url, - echo=True, + echo=False, ) async_session_factory = async_sessionmaker( diff --git a/api/models/__init__.py b/api/models/__init__.py index 71ab449..9d19859 100644 --- a/api/models/__init__.py +++ b/api/models/__init__.py @@ -1,7 +1,7 @@ -from .base import Base -from .user import User +from .base_model import Base +from .user_model import UserModel __all__ = ( "Base", - "User", + "UserModel", ) diff --git a/api/models/base.py b/api/models/base_model.py similarity index 92% rename from api/models/base.py rename to api/models/base_model.py index 75f6318..203f0b5 100644 --- a/api/models/base.py +++ b/api/models/base_model.py @@ -5,6 +5,8 @@ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column class Base(DeclarativeBase): + __abstract__ = True + id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, diff --git a/api/models/user.py b/api/models/user_model.py similarity index 77% rename from api/models/user.py rename to api/models/user_model.py index b9bc7bb..e6960bd 100644 --- a/api/models/user.py +++ b/api/models/user_model.py @@ -1,12 +1,12 @@ from sqlalchemy import Boolean, Column, String from sqlalchemy.orm import Mapped -from api.schemas.user_schema import UserSchema +from api.schemas import UserReadDTO -from .base import Base +from . import Base -class User(Base): +class UserModel(Base): __tablename__ = "users" name: Mapped[str] @@ -22,8 +22,8 @@ class User(Base): f"is_active={self.is_active})>" ) - def to_read_model(self) -> UserSchema: - return UserSchema( + def to_read_model(self) -> UserReadDTO: + return UserReadDTO( id=self.id, name=self.name, ) diff --git a/api/repositories/__init__.py b/api/repositories/__init__.py new file mode 100644 index 0000000..bf53d8f --- /dev/null +++ b/api/repositories/__init__.py @@ -0,0 +1,7 @@ +from .repository import AbstractRepository +from .user import UserRepository + +__all__ = ( + "AbstractRepository", + "UserRepository", +) diff --git a/api/repositories/repository.py b/api/repositories/repository.py new file mode 100644 index 0000000..ff7060e --- /dev/null +++ b/api/repositories/repository.py @@ -0,0 +1,23 @@ +from abc import ABC, abstractmethod + + +class AbstractRepository(ABC): + @abstractmethod + async def add_one(self, data: dict): + raise NotImplementedError() + + @abstractmethod + async def find_all(self): + raise NotImplementedError() + + @abstractmethod + async def find_one(self, filter: dict): + raise NotImplementedError() + + @abstractmethod + async def update_one(self, filter: dict, data: dict): + raise NotImplementedError() + + @abstractmethod + async def delete_one(self, filter: dict): + raise NotImplementedError() diff --git a/api/repositories/user.py b/api/repositories/user.py new file mode 100644 index 0000000..bb0d618 --- /dev/null +++ b/api/repositories/user.py @@ -0,0 +1,30 @@ +from sqlalchemy import insert, select +from sqlalchemy.ext.asyncio.session import AsyncSession + +from api.models import UserModel +from api.repositories import AbstractRepository + + +class UserRepository(AbstractRepository): + def __init__(self, session: AsyncSession): + self.session = session + + async def add_one(self, data: dict): + stmt = insert(UserModel).values(**data) + res = await self.session.execute(stmt) + return res.scalar_one() + + async def find_all(self): + stmt = select(UserModel) + res = await self.session.execute(stmt) + res = [row[0].to_read_model() for row in res.all()] + return res + + async def find_one(self, filter: dict): + return + + async def update_one(self, filter: dict, data: dict): + return + + async def delete_one(self, filter: dict): + return diff --git a/api/repository/user.py b/api/repository/user.py deleted file mode 100644 index 30e23ae..0000000 --- a/api/repository/user.py +++ /dev/null @@ -1,6 +0,0 @@ -import api.models as models -from api.uow.repository import SQLAlchemyRepository - - -class UserRepository(SQLAlchemyRepository): - model = models.User diff --git a/api/router/__init__.py b/api/router/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/api/repository/__init__.py b/api/routers/__init__.py similarity index 100% rename from api/repository/__init__.py rename to api/routers/__init__.py diff --git a/api/router/user.py b/api/routers/user.py similarity index 64% rename from api/router/user.py rename to api/routers/user.py index 311cda8..00c923a 100644 --- a/api/router/user.py +++ b/api/routers/user.py @@ -1,16 +1,16 @@ from fastapi import APIRouter, Depends from api.di import get_user_service -from api.schemas.user_schema import UserSchema -from api.service.user import UserService +from api.schemas import UserReadDTO +from api.services import UserService router = APIRouter() -@router.get("/users", response_model=list[UserSchema]) +@router.get("/users", response_model=list[UserReadDTO]) async def get_user_list( user_service: UserService = Depends(get_user_service), -) -> list[UserSchema]: +) -> list[UserReadDTO]: return await user_service.get_all_users() diff --git a/api/schemas/__init__.py b/api/schemas/__init__.py new file mode 100644 index 0000000..678507a --- /dev/null +++ b/api/schemas/__init__.py @@ -0,0 +1,9 @@ +from .base_schema import ReadDTO, WriteDTO +from .user_schema import UserReadDTO, UserWriteDTO + +__all__ = ( + "WriteDTO", + "ReadDTO", + "UserWriteDTO", + "UserReadDTO", +) diff --git a/api/schemas/base_schema.py b/api/schemas/base_schema.py new file mode 100644 index 0000000..6faffa3 --- /dev/null +++ b/api/schemas/base_schema.py @@ -0,0 +1,13 @@ +from uuid import UUID + +from pydantic import BaseModel + + +class WriteDTO(BaseModel): + + class Config: + from_attributes = True + + +class ReadDTO(WriteDTO): + id: UUID diff --git a/api/schemas/user_schema.py b/api/schemas/user_schema.py index 1b2870e..b59324b 100644 --- a/api/schemas/user_schema.py +++ b/api/schemas/user_schema.py @@ -1,11 +1,9 @@ -from uuid import UUID - -from pydantic import BaseModel +from . import ReadDTO, WriteDTO -class UserSchema(BaseModel): - id: UUID +class UserWriteDTO(WriteDTO): name: str - class Config: - from_attributes = True + +class UserReadDTO(ReadDTO, UserWriteDTO): + pass diff --git a/api/service/__init__.py b/api/service/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/api/services/__init__.py b/api/services/__init__.py new file mode 100644 index 0000000..b30026f --- /dev/null +++ b/api/services/__init__.py @@ -0,0 +1,3 @@ +from .user import UserService + +__all__ = ("UserService",) diff --git a/api/service/user.py b/api/services/user.py similarity index 100% rename from api/service/user.py rename to api/services/user.py diff --git a/api/uow/repository.py b/api/uow/repository.py deleted file mode 100644 index d0025dc..0000000 --- a/api/uow/repository.py +++ /dev/null @@ -1,26 +0,0 @@ -from typing import Generic, TypeVar - -from sqlalchemy import insert, select -from sqlalchemy.ext.asyncio import AsyncSession - -import api.models as models - -ModelType = TypeVar("ModelType", bound=models.Base) - - -class SQLAlchemyRepository(Generic[ModelType]): - model: type[ModelType] - - def __init__(self, session: AsyncSession): - self.session = session - - async def add_one(self, data: dict) -> ModelType: - stmt = insert(self.model).values(**data) - res = await self.session.execute(stmt) - return res.scalar_one() - - async def find_all(self) -> list[ModelType]: - stmt = select(self.model) - res = await self.session.execute(stmt) - res = [row[0].to_read_model() for row in res.all()] - return res diff --git a/api/uow/uow_base.py b/api/uow/uow_base.py index fafe3e2..1f46aca 100644 --- a/api/uow/uow_base.py +++ b/api/uow/uow_base.py @@ -1,4 +1,4 @@ -from api.repository.user import UserRepository +from api.repositories import UserRepository class UnitOfWork: @@ -9,6 +9,8 @@ class UnitOfWork: self.session = self.session_factory() self.users = UserRepository(self.session) + print("session id:", id(self.session)) + print("UoW obj id:", id(self)) async def __aexit__(self, *args): await self.session.rollback()