From b43ac44da9ac82c263d93bf27b18331698247aae Mon Sep 17 00:00:00 2001 From: Artem Litvinov Date: Sat, 23 Sep 2023 21:17:45 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B9=D0=BA=D0=B8=20=D0=91=D0=94=20&=20async=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B8=D0=B5=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fastapi_app/lib/app/settings.py | 2 +- .../lib/app/split_settings/__init__.py | 5 ++- .../lib/app/split_settings/postgres.py | 19 +++++++++- src/fastapi_app/lib/db/base.py | 10 ++++-- src/fastapi_app/lib/db/postgres.py | 35 ++++++++----------- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/fastapi_app/lib/app/settings.py b/src/fastapi_app/lib/app/settings.py index e3853ea..22149c4 100644 --- a/src/fastapi_app/lib/app/settings.py +++ b/src/fastapi_app/lib/app/settings.py @@ -8,7 +8,7 @@ import lib.app.split_settings as app_split_settings class Settings(pydantic_settings.BaseSettings): api: app_split_settings.ApiSettings = pydantic.Field(default_factory=lambda: app_split_settings.ApiSettings()) - postgres: app_split_settings.PostgresSettings = pydantic.Field( + db: app_split_settings.PostgresSettings = pydantic.Field( default_factory=lambda: app_split_settings.PostgresSettings() ) logger: app_split_settings.LoggingSettings = pydantic.Field( diff --git a/src/fastapi_app/lib/app/split_settings/__init__.py b/src/fastapi_app/lib/app/split_settings/__init__.py index 627e186..3099e3f 100644 --- a/src/fastapi_app/lib/app/split_settings/__init__.py +++ b/src/fastapi_app/lib/app/split_settings/__init__.py @@ -1,12 +1,15 @@ from .api import * from .logger import * from .postgres import * +from .postgres import DBSettings, PostgresSettings from .project import * __all__ = [ "ApiSettings", + "DBSettings", "LoggingSettings", - "get_logging_config", + "PostgresSettings", "PostgresSettings", "ProjectSettings", + "get_logging_config", ] diff --git a/src/fastapi_app/lib/app/split_settings/postgres.py b/src/fastapi_app/lib/app/split_settings/postgres.py index 62452f6..1a33f64 100644 --- a/src/fastapi_app/lib/app/split_settings/postgres.py +++ b/src/fastapi_app/lib/app/split_settings/postgres.py @@ -4,7 +4,23 @@ import pydantic_settings import lib.app.split_settings.utils as app_split_settings_utils -class PostgresSettings(pydantic_settings.BaseSettings): +class DBSettings(pydantic_settings.BaseSettings): + """Abstract class for database settings.""" + + protocol: str + name: str + host: str + port: int + user: str + password: pydantic.SecretStr + + @property + def dsn(self) -> str: + """Get database DSN.""" + return f"{self.protocol}://{self.user}:{self.password}@{self.host}:{self.port}/{self.name}" + + +class PostgresSettings(DBSettings): model_config = pydantic_settings.SettingsConfigDict( env_file=app_split_settings_utils.ENV_PATH, env_prefix="POSTGRES_", @@ -12,6 +28,7 @@ class PostgresSettings(pydantic_settings.BaseSettings): extra="ignore", ) + protocol: str = "postgresql+asyncpg" name: str = "database_name" host: str = "localhost" port: int = 5432 diff --git a/src/fastapi_app/lib/db/base.py b/src/fastapi_app/lib/db/base.py index c4434ed..c13548e 100644 --- a/src/fastapi_app/lib/db/base.py +++ b/src/fastapi_app/lib/db/base.py @@ -1,3 +1,7 @@ -# from libs.api.models.movies import * -# from libs.api.models.notification_templates import * -# from libs.api.models.users import * +import sqlalchemy.orm + + +class Base(sqlalchemy.orm.DeclarativeBase): + """Base Model Class for ORM Models and Alembic.""" + + pass diff --git a/src/fastapi_app/lib/db/postgres.py b/src/fastapi_app/lib/db/postgres.py index ecefe41..bd4889d 100644 --- a/src/fastapi_app/lib/db/postgres.py +++ b/src/fastapi_app/lib/db/postgres.py @@ -1,38 +1,31 @@ import typing -from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine -from sqlalchemy.orm import DeclarativeBase +import sqlalchemy.ext.asyncio as sa_asyncio import lib.app.settings as app_settings -settings = app_settings.settings - -# Создаём базовый класс для будущих моделей +# settings = app_settings.settings -class Base(DeclarativeBase): - pass - - -# Создаём движок -# Настройки подключения к БД передаём из переменных окружения, которые заранее загружены в файл настроек class AsyncDB: - def __init__(self): - self.database_dsn = ( - f"postgresql+asyncpg://{settings.db.user}:{settings.db.password}" - f"@{settings.db.host}:{settings.db.port}/{settings.db.name}" + """Async DB connection.""" + + def __init__(self, settings: app_settings.Settings): + self.database_dsn = settings.db.dsn + self.engine = sa_asyncio.create_async_engine(self.database_dsn, echo=settings.project.debug, future=True) + self.async_session = sa_asyncio.async_sessionmaker( + self.engine, class_=sa_asyncio.AsyncSession, expire_on_commit=False ) - self.engine = create_async_engine(self.database_dsn, echo=settings.project.debug, future=True) - self.async_session = async_sessionmaker(self.engine, class_=AsyncSession, expire_on_commit=False) -db = AsyncDB() - - -async def get_session() -> typing.AsyncGenerator[AsyncSession, typing.Any]: +async def get_session(settings: app_settings.Settings) -> typing.AsyncGenerator[sa_asyncio.AsyncSession, typing.Any]: + db = AsyncDB(settings) async with db.async_session() as session: try: yield session except Exception: await session.rollback() raise + finally: + await session.close() + await db.engine.dispose()