__all__ = [ 'BaseRepository', 'T_Model', ] from abc import ABC, abstractmethod from typing import Generic, Optional, Type, TypeVar from sqlalchemy import select from sqlalchemy.orm import Session from tofu_api.models import BaseModel from .exceptions import ObjectNotFoundException T_Model = TypeVar('T_Model', bound=BaseModel) class BaseRepository(Generic[T_Model], ABC): """ Base class for repositories. """ # Database session session: Session @property @abstractmethod def model_cls(self) -> Type[T_Model]: """ Set this to the model class. """ raise NotImplementedError def __init__(self, *, session: Session): self.session = session @staticmethod def _or_raise(resource: Optional[T_Model], exception_msg: Optional[str] = None) -> T_Model: if resource is None: raise ObjectNotFoundException(exception_msg) return resource def fetch_by_id(self, resource_id: int) -> T_Model: """ Fetches a resource by ID. Raises an ObjectNotFoundException if no resource with the ID was found. """ resource = self.session.get(self.model_cls, resource_id) return self._or_raise(resource, f'Resource with ID {resource_id} was not found.') def fetch_all(self) -> list[T_Model]: """ Fetches all resources of the repository type. """ return self.session.scalars( select(self.model_cls) ).all() def commit_session(self) -> None: """ Commits the current database session. """ self.commit_session() def rollback_session(self) -> None: """ Rolls back the current database session. """ self.rollback_session() def save_resource(self, *resources: T_Model, commit: bool = True) -> None: """ Saves one or multiple resources to the database by adding them to the session and committing the session. Set `commit` to False to skip committing (the session will still be flushed, though). """ for resource in resources: self.session.add(resource) self.session.flush() if commit: self.session.commit() def delete_resource(self, *resources: T_Model, commit: bool = True) -> None: """ Deletes one or multiple resources from the database. Set `commit` to False to skip committing (the session will still be flushed, though). """ for resource in resources: self.session.delete(resource) self.session.flush() if commit: self.session.commit()