import abc from collections.abc import Sequence from dataclasses import dataclass, field from typing import Any, ClassVar, Generic, TypeVar, cast, get_args import sqlalchemy as sa from sqlalchemy import Result from sqlalchemy.orm.interfaces import ORMOption from .base import Database T = TypeVar("T") @dataclass class Repository(abc.ABC, Generic[T]): db: Database model: type[T] = field(init=False) default_options: ClassVar[Sequence[ORMOption]] = [] def __post_init__(self) -> None: orig_bases = getattr(self.__class__, "__orig_bases__") self.model = cast(type[T], get_args(orig_bases[0])[0]) async def all(self) -> list[T]: query = sa.select(self.model).options(*self.default_options) result: Result[Any] = await self.db.session.execute(query) return list(result.scalars().all()) async def get(self, ident: Any) -> T | None: return await self.db.session.get( self.model, ident=ident, options=self.default_options ) async def first(self) -> T | None: query = sa.select(self.model).options(*self.default_options) result: Result[Any] = await self.db.session.execute(query) return result.scalars().first() async def count(self) -> int: query = sa.select(sa.func.count()).select_from(sa.select(self.model).subquery()) result: Any = await self.db.session.execute(query) return cast(int, result.scalar_one()) def add(self, obj: Any) -> None: self.db.session.add(obj) async def delete(self, obj: Any) -> None: await self.db.session.delete(obj)