From f9631a712b57b6352636219148dd30f9cb7f0341 Mon Sep 17 00:00:00 2001 From: pi3c Date: Wed, 6 Mar 2024 03:59:16 +0300 Subject: [PATCH] uow and di basic implementation --- api/migrations/env.py | 4 ++-- ...0cb4f18_initial.py => 3ba730985688_initial.py} | 9 +++++---- api/model/__init__.py | 0 api/models/__init__.py | 7 +++++++ api/{model => models}/base.py | 0 api/{model => models}/user.py | 10 ++++++++++ api/repository/user.py | 4 ++-- api/router/user.py | 5 +++-- api/schemas/user_schema.py | 11 +++++++++++ api/service/user.py | 4 +++- api/uow/repository.py | 15 +++++++-------- 11 files changed, 50 insertions(+), 19 deletions(-) rename api/migrations/versions/{ec1380cb4f18_initial.py => 3ba730985688_initial.py} (80%) delete mode 100644 api/model/__init__.py create mode 100644 api/models/__init__.py rename api/{model => models}/base.py (100%) rename api/{model => models}/user.py (66%) create mode 100644 api/schemas/user_schema.py diff --git a/api/migrations/env.py b/api/migrations/env.py index 9e66d19..8049e3c 100644 --- a/api/migrations/env.py +++ b/api/migrations/env.py @@ -3,7 +3,7 @@ from logging.config import fileConfig from alembic import context from sqlalchemy import engine_from_config, pool -import api.model.user # type: ignore +import api.models as models # this is the Alembic Config object, which provides # access to the values within the .ini file in use. @@ -18,7 +18,7 @@ if config.config_file_name is not None: # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata -target_metadata = api.model.user.Base.metadata +target_metadata = models.Base.metadata # other values from the config, defined by the needs of env.py, # can be acquired: diff --git a/api/migrations/versions/ec1380cb4f18_initial.py b/api/migrations/versions/3ba730985688_initial.py similarity index 80% rename from api/migrations/versions/ec1380cb4f18_initial.py rename to api/migrations/versions/3ba730985688_initial.py index 30b9aa0..89b0e73 100644 --- a/api/migrations/versions/ec1380cb4f18_initial.py +++ b/api/migrations/versions/3ba730985688_initial.py @@ -1,8 +1,8 @@ """initial -Revision ID: ec1380cb4f18 +Revision ID: 3ba730985688 Revises: -Create Date: 2024-03-04 03:11:36.206211 +Create Date: 2024-03-06 03:10:09.050166 """ @@ -12,7 +12,7 @@ import sqlalchemy as sa from alembic import op # revision identifiers, used by Alembic. -revision: str = "ec1380cb4f18" +revision: str = "3ba730985688" down_revision: str | None = None branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None @@ -22,10 +22,11 @@ def upgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### op.create_table( "users", - sa.Column("id", sa.Integer(), nullable=False), sa.Column("email", sa.String(), nullable=True), sa.Column("hashed_password", sa.String(), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=True), + sa.Column("name", sa.String(), nullable=False), + sa.Column("id", sa.UUID(), nullable=False), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("email"), ) diff --git a/api/model/__init__.py b/api/model/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/api/models/__init__.py b/api/models/__init__.py new file mode 100644 index 0000000..71ab449 --- /dev/null +++ b/api/models/__init__.py @@ -0,0 +1,7 @@ +from .base import Base +from .user import User + +__all__ = ( + "Base", + "User", +) diff --git a/api/model/base.py b/api/models/base.py similarity index 100% rename from api/model/base.py rename to api/models/base.py diff --git a/api/model/user.py b/api/models/user.py similarity index 66% rename from api/model/user.py rename to api/models/user.py index c665b2e..b9bc7bb 100644 --- a/api/model/user.py +++ b/api/models/user.py @@ -1,4 +1,7 @@ from sqlalchemy import Boolean, Column, String +from sqlalchemy.orm import Mapped + +from api.schemas.user_schema import UserSchema from .base import Base @@ -6,6 +9,7 @@ from .base import Base class User(Base): __tablename__ = "users" + name: Mapped[str] email = Column(String, unique=True) hashed_password = Column(String) is_active = Column(Boolean, default=True) @@ -17,3 +21,9 @@ class User(Base): f'hashed_password="{self.hashed_password}", ' f"is_active={self.is_active})>" ) + + def to_read_model(self) -> UserSchema: + return UserSchema( + id=self.id, + name=self.name, + ) diff --git a/api/repository/user.py b/api/repository/user.py index 32321fc..30e23ae 100644 --- a/api/repository/user.py +++ b/api/repository/user.py @@ -1,6 +1,6 @@ -from api.model.user import User +import api.models as models from api.uow.repository import SQLAlchemyRepository class UserRepository(SQLAlchemyRepository): - model = User + model = models.User diff --git a/api/router/user.py b/api/router/user.py index f233579..284f8b9 100644 --- a/api/router/user.py +++ b/api/router/user.py @@ -2,16 +2,17 @@ from dependency_injector.wiring import Provide, inject from fastapi import APIRouter, Depends from api.di import Container +from api.schemas.user_schema import UserSchema from api.service.user import UserService router = APIRouter() -@router.get("/users") +@router.get("/users", response_model=list[UserSchema]) @inject async def get_user_list( user_service: UserService = Depends(Provide[Container.user_service]), -): +) -> list[UserSchema]: return await user_service.get_all_users() diff --git a/api/schemas/user_schema.py b/api/schemas/user_schema.py new file mode 100644 index 0000000..1b2870e --- /dev/null +++ b/api/schemas/user_schema.py @@ -0,0 +1,11 @@ +from uuid import UUID + +from pydantic import BaseModel + + +class UserSchema(BaseModel): + id: UUID + name: str + + class Config: + from_attributes = True diff --git a/api/service/user.py b/api/service/user.py index 8587910..80c626b 100644 --- a/api/service/user.py +++ b/api/service/user.py @@ -7,4 +7,6 @@ class UserService: async def get_all_users(self): async with self.uow: - await self.uow.users.find_all() + res = await self.uow.users.find_all() + + return res diff --git a/api/uow/repository.py b/api/uow/repository.py index de1745d..7aee131 100644 --- a/api/uow/repository.py +++ b/api/uow/repository.py @@ -1,23 +1,22 @@ from abc import ABC, abstractmethod from typing import Generic, TypeVar -from uuid import UUID from sqlalchemy import insert, select from sqlalchemy.ext.asyncio import AsyncSession -from api.model.base import Base +import api.models as models -ModelType = TypeVar("ModelType", bound=Base) +ModelType = TypeVar("ModelType", bound=models.Base) class AbstractRepository(ABC): @abstractmethod async def add_one(self, data: dict): - raise NotImplementedError + raise NotImplementedError() @abstractmethod async def find_all(self): - raise NotImplementedError + raise NotImplementedError() class SQLAlchemyRepository(AbstractRepository, Generic[ModelType]): @@ -26,12 +25,12 @@ class SQLAlchemyRepository(AbstractRepository, Generic[ModelType]): def __init__(self, session: AsyncSession): self.session = session - async def add_one(self, data: dict) -> UUID: - stmt = insert(self.model).values(**data).returning(self.model.id) + 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): + 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()]