42 lines
1.3 KiB
Python
42 lines
1.3 KiB
Python
"""SQLAlchemy-Basis, Engine und Sitzungen."""
|
|
|
|
from collections.abc import Iterator
|
|
from contextlib import contextmanager
|
|
from datetime import UTC, datetime
|
|
|
|
from sqlalchemy import DateTime, MetaData, create_engine
|
|
from sqlalchemy.engine import Engine
|
|
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker
|
|
|
|
# Einheitliche Namen für Constraints, damit Alembic-Migrationen stabil bleiben
|
|
NAMING = {
|
|
"ix": "ix_%(column_0_label)s",
|
|
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
|
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
|
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
|
"pk": "pk_%(table_name)s",
|
|
}
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
metadata = MetaData(naming_convention=NAMING)
|
|
type_annotation_map = {datetime: DateTime(timezone=True)}
|
|
|
|
|
|
def now() -> datetime:
|
|
return datetime.now(UTC)
|
|
|
|
|
|
class Database:
|
|
def __init__(self, url: str):
|
|
self.engine: Engine = create_engine(url, pool_pre_ping=True)
|
|
self.sessions = sessionmaker(self.engine, expire_on_commit=False)
|
|
|
|
@contextmanager
|
|
def session(self) -> Iterator[Session]:
|
|
"""Eine Transaktion: commit am Ende, rollback bei Fehler."""
|
|
with self.sessions() as session, session.begin():
|
|
yield session
|
|
|
|
def dispose(self) -> None:
|
|
self.engine.dispose()
|