Files
dibbler/dibbler/models/Base.py
h7x4 2c9f0213c1
Some checks failed
Run tests / run-tests (push) Failing after 24s
Run benchmarks / run-tests (push) Failing after 24s
WIP: caching
2026-02-12 16:49:22 +09:00

56 lines
1.9 KiB
Python

from sqlalchemy import MetaData
from sqlalchemy.orm import (
DeclarativeBase,
declared_attr,
)
from sqlalchemy.orm.collections import (
InstrumentedDict,
InstrumentedList,
InstrumentedSet,
)
def _pascal_case_to_snake_case(name: str) -> str:
return "".join(["_" + i.lower() if i.isupper() else i for i in name]).lstrip("_")
class Base(DeclarativeBase):
metadata = MetaData(
naming_convention={
"ix": "ix__%(table_name)s__%(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",
}
)
@declared_attr.directive
def __tablename__(cls) -> str:
if hasattr(cls, "__table_name__"):
assert isinstance(cls.__table_name__, str)
return cls.__table_name__
return _pascal_case_to_snake_case(cls.__name__)
# NOTE: This is the default implementation of __repr__ for all tables,
# but it is preferable to override it for each table to get a nicer
# looking representation. This trades a bit of messiness for a complete
# output of all relevant fields.
def __repr__(self) -> str:
columns = ", ".join(
f"{k}={repr(v)}"
for k, v in self.__dict__.items()
if not any(
[
k.startswith("_"),
# Ensure that we don't try to print out the entire list of
# relationships, which could create an infinite loop
isinstance(v, Base),
isinstance(v, InstrumentedList),
isinstance(v, InstrumentedSet),
isinstance(v, InstrumentedDict),
],
)
)
return f"<{self.__class__.__name__}({columns})>"