From 71f9db6c0dd0d2c12c85bcb14f5d7543166923f2 Mon Sep 17 00:00:00 2001 From: Daniil Fajnberg Date: Fri, 1 Jul 2022 10:46:48 +0200 Subject: [PATCH] Add ORM base class; make Postgresql mandatory; add dependencies --- requirements/common.txt | 4 +++- setup.cfg | 2 ++ src/compub/db.py | 9 --------- src/compub/db/__init__.py | 1 + src/compub/db/base.py | 31 +++++++++++++++++++++++++++++++ src/compub/exceptions.py | 6 ++++++ src/compub/settings.py | 8 ++------ 7 files changed, 45 insertions(+), 16 deletions(-) delete mode 100644 src/compub/db.py create mode 100644 src/compub/db/__init__.py create mode 100644 src/compub/db/base.py create mode 100644 src/compub/exceptions.py diff --git a/requirements/common.txt b/requirements/common.txt index 9c1bf34..4deb891 100644 --- a/requirements/common.txt +++ b/requirements/common.txt @@ -1,4 +1,6 @@ Pydantic FastAPI SQLAlchemy[asyncio] -Alembic \ No newline at end of file +Alembic +SQLAlchemy-Utils +python-slugify \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 4dd5231..7c4c265 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,8 @@ install_requires = FastAPI SQLAlchemy[asyncio] Alembic + SQLAlchemy-Utils + python-slugify [options.extras_require] srv = diff --git a/src/compub/db.py b/src/compub/db.py deleted file mode 100644 index c0ad8c9..0000000 --- a/src/compub/db.py +++ /dev/null @@ -1,9 +0,0 @@ -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession -from sqlalchemy.orm import declarative_base, sessionmaker - -from compub.settings import settings - - -engine = create_async_engine(settings.db_uri, future=True) -LocalAsyncSession = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) -ORMBase = declarative_base() diff --git a/src/compub/db/__init__.py b/src/compub/db/__init__.py new file mode 100644 index 0000000..3b794e3 --- /dev/null +++ b/src/compub/db/__init__.py @@ -0,0 +1 @@ +from .base import LocalAsyncSession, ORMBase, engine diff --git a/src/compub/db/base.py b/src/compub/db/base.py new file mode 100644 index 0000000..906b7e5 --- /dev/null +++ b/src/compub/db/base.py @@ -0,0 +1,31 @@ +from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession +from sqlalchemy.orm import declarative_base, sessionmaker +from sqlalchemy.sql.functions import now as db_now +from sqlalchemy.sql.schema import Column +from sqlalchemy.sql.sqltypes import TIMESTAMP + +from compub.exceptions import NoDatabaseConfigured +from compub.settings import settings + + +if settings.db_uri is None: + raise NoDatabaseConfigured +engine = create_async_engine(settings.db_uri, future=True) +LocalAsyncSession = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession) +ORMBase = declarative_base() + + +class AbstractBase(ORMBase): + __abstract__ = True + + NON_REPR_FIELDS = ['id', 'date_created', 'date_updated'] + + date_created = Column(TIMESTAMP(timezone=False), server_default=db_now()) + date_updated = Column(TIMESTAMP(timezone=False), server_default=db_now(), onupdate=db_now()) + + def __repr__(self) -> str: + # Exclude non-representative fields: + fields = (name for name in self.__mapper__.columns.keys() if name not in self.NON_REPR_FIELDS) + # Exclude NULL value fields: + attrs = ', '.join(f"{name}={repr(getattr(self, name))}" for name in fields if getattr(self, name) is not None) + return f"{self.__class__.__name__}({attrs})" diff --git a/src/compub/exceptions.py b/src/compub/exceptions.py new file mode 100644 index 0000000..958c86f --- /dev/null +++ b/src/compub/exceptions.py @@ -0,0 +1,6 @@ +class ConfigError(Exception): + pass + + +class NoDatabaseConfigured(ConfigError): + pass diff --git a/src/compub/settings.py b/src/compub/settings.py index 764c3b5..67c04d5 100644 --- a/src/compub/settings.py +++ b/src/compub/settings.py @@ -3,7 +3,7 @@ import logging.config from pathlib import Path from typing import Any, Callable, ClassVar -from pydantic import BaseModel, BaseSettings, AnyUrl, validator +from pydantic import BaseModel, BaseSettings, PostgresDsn, validator from pydantic.env_settings import SettingsSourceCallable from yaml import safe_load @@ -70,10 +70,6 @@ def _yaml_config_settings_source(settings_obj: AbstractBaseSettings) -> dict[str return config -class DBUri(AnyUrl): - host_required = False - - class ServerSettings(BaseModel): host: str = '127.0.0.1' port: int = 9009 @@ -81,7 +77,7 @@ class ServerSettings(BaseModel): class Settings(AbstractBaseSettings): - db_uri: DBUri = 'sqlite+aiosqlite:///:memory:' + db_uri: PostgresDsn | None = None server: ServerSettings = ServerSettings() log_config: dict | Path | None = None