from collections.abc import Iterable from typing import Any, ClassVar, Self, cast from sqlalchemy.orm import DeclarativeBase, MappedAsDataclass from sqlalchemy.sql.selectable import ForUpdateParameter from fansifter_common.adapters.db.base import Database from .query import Query from .utils import delete_instance, refresh_instance, save_instance class ModelMixin: """Mixin that adds save/delete/refresh/query to any DeclarativeBase subclass. Usage in a project:: class Model(ModelMixin, MappedAsDataclass, DeclarativeBase): __db__ = db type_annotation_map = {str: sa.Text, datetime: NaiveUTCDateTime} """ __db__: ClassVar[Database] # --- Instance methods --- def save(self, *, flush: bool = False) -> Self: return save_instance(self.__db__, instance=self, flush=flush) def delete(self, *, flush: bool = False) -> None: delete_instance(self.__db__, instance=self, flush=flush) def refresh( self, *, attribute_names: Iterable[str] | None = None, with_for_update: ForUpdateParameter = None, ) -> None: refresh_instance( self.__db__, instance=cast(Any, self), attribute_names=attribute_names, with_for_update=with_for_update, ) # --- Descriptors --- query = Query[Self].as_descriptor() class Model(ModelMixin, MappedAsDataclass, DeclarativeBase): """Ready-to-use base model. Set ``__db__`` on this class to configure the database.""" __db__: ClassVar[Database]