feat: Projektfundament mit Datenmodell, Migrationen und Seed
- Repo-Struktur für Backend, Frontend, Dokumentation und Gitea-Workflows - FastAPI-Skeleton mit pydantic-settings und RFC-7807-artigem Fehlerformat - Vollständiges Datenmodell (15 Tabellen) als SQLAlchemy-2.0-Modelle - Alembic gegen die Async-Engine inklusive Erstmigration - Idempotenter Seed für den deutschen Kategoriebaum und Standardregeln - Endpunkte /api/health und /api/version - pytest-Infrastruktur mit transaktionsisolierten Fixtures Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"""Deklarative Basisklasse und gemeinsame Spaltentypen."""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import DateTime, MetaData, Numeric
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
|
||||
# Feste Namenskonventionen, damit Alembic stabile Constraint-Namen erzeugt.
|
||||
NAMING_CONVENTION = {
|
||||
"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",
|
||||
}
|
||||
|
||||
# Alle Geldbeträge einheitlich als NUMERIC(12,2) -> Decimal.
|
||||
Money = Numeric(12, 2, asdecimal=True)
|
||||
|
||||
|
||||
def utcnow() -> datetime:
|
||||
"""Zeitstempel in UTC – die Anzeige rechnet nach Europe/Berlin um."""
|
||||
return datetime.now(UTC)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
metadata = MetaData(naming_convention=NAMING_CONVENTION)
|
||||
|
||||
type_annotation_map = { # noqa: RUF012
|
||||
Decimal: Money,
|
||||
}
|
||||
|
||||
|
||||
class TimestampMixin:
|
||||
"""created_at/updated_at für Tabellen mit Änderungsverfolgung."""
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utcnow, nullable=False
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False
|
||||
)
|
||||
|
||||
|
||||
class CreatedAtMixin:
|
||||
"""Nur created_at für rein additive Tabellen."""
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utcnow, nullable=False
|
||||
)
|
||||
Reference in New Issue
Block a user