from sqlalchemy.orm import DeclarativeBase, declared_attr, Mapped, mapped_column, relationship, add_mapped_attribute def to_snake_case(name: str) -> str: return "".join(["_" + i.lower() if i.isupper() else i for i in name]).lstrip("_") def bidirectional_relationship(cls, foreign_table_cls): """Create a bidirectional relationship between two table-classes.""" column_name = f"{to_snake_case(cls.__name__)}" column_name = f"{column_name}es" if column_name.endswith("s") else f"{column_name}s" add_mapped_attribute( foreign_table_cls, column_name, relationship(cls, back_populates=foreign_table_cls.__tablename__, cascade="all, delete-orphan" ) ) return relationship(foreign_table_cls.__name__, back_populates=column_name) class Base(DeclarativeBase): __abstract__ = True id: Mapped[int] = mapped_column(primary_key=True, sort_order=-1000000) # noinspection PyMethodParameters @declared_attr.directive def __tablename__(cls) -> str: """Default table name in Database is derived from Class Name""" return to_snake_case(cls.__name__) # noinspection PyPep8Naming