Add ORM base class; make Postgresql mandatory; add dependencies

This commit is contained in:
Daniil Fajnberg 2022-07-01 10:46:48 +02:00
parent eb08e5a674
commit 71f9db6c0d
7 changed files with 45 additions and 16 deletions

View File

@ -2,3 +2,5 @@ Pydantic
FastAPI FastAPI
SQLAlchemy[asyncio] SQLAlchemy[asyncio]
Alembic Alembic
SQLAlchemy-Utils
python-slugify

View File

@ -27,6 +27,8 @@ install_requires =
FastAPI FastAPI
SQLAlchemy[asyncio] SQLAlchemy[asyncio]
Alembic Alembic
SQLAlchemy-Utils
python-slugify
[options.extras_require] [options.extras_require]
srv = srv =

View File

@ -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()

View File

@ -0,0 +1 @@
from .base import LocalAsyncSession, ORMBase, engine

31
src/compub/db/base.py Normal file
View File

@ -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})"

6
src/compub/exceptions.py Normal file
View File

@ -0,0 +1,6 @@
class ConfigError(Exception):
pass
class NoDatabaseConfigured(ConfigError):
pass

View File

@ -3,7 +3,7 @@ import logging.config
from pathlib import Path from pathlib import Path
from typing import Any, Callable, ClassVar 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 pydantic.env_settings import SettingsSourceCallable
from yaml import safe_load from yaml import safe_load
@ -70,10 +70,6 @@ def _yaml_config_settings_source(settings_obj: AbstractBaseSettings) -> dict[str
return config return config
class DBUri(AnyUrl):
host_required = False
class ServerSettings(BaseModel): class ServerSettings(BaseModel):
host: str = '127.0.0.1' host: str = '127.0.0.1'
port: int = 9009 port: int = 9009
@ -81,7 +77,7 @@ class ServerSettings(BaseModel):
class Settings(AbstractBaseSettings): class Settings(AbstractBaseSettings):
db_uri: DBUri = 'sqlite+aiosqlite:///:memory:' db_uri: PostgresDsn | None = None
server: ServerSettings = ServerSettings() server: ServerSettings = ServerSettings()
log_config: dict | Path | None = None log_config: dict | Path | None = None