Files
Aurora-Core/app/models/base.py
T

81 lines
2.4 KiB
Python
Raw Normal View History

2026-09-04 17:34:03 -05:00
"""Declarative base and shared mixins for all Aurora Core models."""
from __future__ import annotations
import uuid
from datetime import datetime
from sqlalchemy import DateTime, Enum, MetaData, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from uuid6 import uuid7
from app.models.enums import (
ArtworkType,
CompanyType,
IngestionJobStatus,
MatchConfidence,
MovieStatus,
SeriesStatus,
VideoType,
)
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",
}
def _enum_column(enum_cls: type) -> Enum:
"""Store our str-Enum members by `.value` (lowercase snake_case), not
`.name`, which is what SQLAlchemy's Enum type does by default. Without
this, the DB would store e.g. "RELEASED" while every part of the
codebase (schemas, ingestion, API filters) works with "released"."""
return Enum(
enum_cls,
values_callable=lambda obj: [e.value for e in obj],
name=f"{enum_cls.__name__.lower()}_enum",
)
class Base(DeclarativeBase):
"""Base class for all ORM models. Naming convention keeps Alembic's
autogenerated constraint names stable across databases."""
metadata = MetaData(naming_convention=NAMING_CONVENTION)
type_annotation_map = {
MovieStatus: _enum_column(MovieStatus),
SeriesStatus: _enum_column(SeriesStatus),
ArtworkType: _enum_column(ArtworkType),
VideoType: _enum_column(VideoType),
CompanyType: _enum_column(CompanyType),
IngestionJobStatus: _enum_column(IngestionJobStatus),
MatchConfidence: _enum_column(MatchConfidence),
}
class UUIDPrimaryKeyMixin:
"""UUIDv7 primary key. Time-ordered so it stays btree-friendly and can
double as a creation-order cursor, unlike a random UUIDv4."""
id: Mapped[uuid.UUID] = mapped_column(
primary_key=True,
default=lambda: uuid.UUID(str(uuid7())),
)
class TimestampMixin:
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
onupdate=func.now(),
nullable=False,
)