aiteamutils 0.2.73__tar.gz → 0.2.76__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aiteamutils-0.2.73 → aiteamutils-0.2.76}/PKG-INFO +1 -1
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/base_model.py +12 -4
 - aiteamutils-0.2.76/aiteamutils/base_repository.py +146 -0
 - aiteamutils-0.2.76/aiteamutils/base_service.py +256 -0
 - aiteamutils-0.2.76/aiteamutils/database.py +585 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/exceptions.py +6 -2
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/security.py +32 -47
 - aiteamutils-0.2.76/aiteamutils/version.py +2 -0
 - aiteamutils-0.2.73/aiteamutils/base_repository.py +0 -63
 - aiteamutils-0.2.73/aiteamutils/base_service.py +0 -77
 - aiteamutils-0.2.73/aiteamutils/database.py +0 -242
 - aiteamutils-0.2.73/aiteamutils/version.py +0 -2
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/.cursorrules +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/.gitignore +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/README.md +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/__init__.py +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/cache.py +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/config.py +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/enums.py +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/aiteamutils/validators.py +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/pyproject.toml +0 -0
 - {aiteamutils-0.2.73 → aiteamutils-0.2.76}/setup.py +0 -0
 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            from datetime import datetime, timezone
         
     | 
| 
      
 1 
     | 
    
         
            +
            from datetime import datetime, timedelta, timezone
         
     | 
| 
       2 
2 
     | 
    
         
             
            from typing import Any, Dict, TypeVar, Generic, Optional
         
     | 
| 
       3 
3 
     | 
    
         
             
            from ulid import ULID
         
     | 
| 
       4 
4 
     | 
    
         
             
            from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
         
     | 
| 
         @@ -23,10 +23,18 @@ class BaseColumn(Base): 
     | 
|
| 
       23 
23 
     | 
    
         
             
                    doc="ULID",
         
     | 
| 
       24 
24 
     | 
    
         
             
                    nullable=False
         
     | 
| 
       25 
25 
     | 
    
         
             
                )
         
     | 
| 
       26 
     | 
    
         
            -
                created_at: Mapped[datetime] = mapped_column( 
     | 
| 
      
 26 
     | 
    
         
            +
                created_at: Mapped[datetime] = mapped_column(
         
     | 
| 
      
 27 
     | 
    
         
            +
                    default=datetime.now(timezone.utc),
         
     | 
| 
      
 28 
     | 
    
         
            +
                    index=True
         
     | 
| 
      
 29 
     | 
    
         
            +
                )
         
     | 
| 
       27 
30 
     | 
    
         
             
                updated_at: Mapped[datetime] = mapped_column(
         
     | 
| 
       28 
     | 
    
         
            -
                    default=datetime. 
     | 
| 
       29 
     | 
    
         
            -
                    onupdate=datetime. 
     | 
| 
      
 31 
     | 
    
         
            +
                    default=datetime.now(timezone.utc),
         
     | 
| 
      
 32 
     | 
    
         
            +
                    onupdate=datetime.now(timezone.utc),
         
     | 
| 
      
 33 
     | 
    
         
            +
                    index=True
         
     | 
| 
      
 34 
     | 
    
         
            +
                )
         
     | 
| 
      
 35 
     | 
    
         
            +
                deleted_at: Mapped[datetime] = mapped_column(
         
     | 
| 
      
 36 
     | 
    
         
            +
                    default=None,
         
     | 
| 
      
 37 
     | 
    
         
            +
                    nullable=True
         
     | 
| 
       30 
38 
     | 
    
         
             
                )
         
     | 
| 
       31 
39 
     | 
    
         
             
                is_deleted: Mapped[bool] = mapped_column(
         
     | 
| 
       32 
40 
     | 
    
         
             
                    default=False,
         
     | 
| 
         @@ -0,0 +1,146 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #기본 라이브러리
         
     | 
| 
      
 2 
     | 
    
         
            +
            from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
         
     | 
| 
      
 3 
     | 
    
         
            +
            from sqlalchemy.orm import DeclarativeBase
         
     | 
| 
      
 4 
     | 
    
         
            +
            from sqlalchemy.ext.asyncio import AsyncSession
         
     | 
| 
      
 5 
     | 
    
         
            +
            from sqlalchemy.exc import SQLAlchemyError
         
     | 
| 
      
 6 
     | 
    
         
            +
            from sqlalchemy import select
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            #패키지 라이브러리
         
     | 
| 
      
 9 
     | 
    
         
            +
            from .exceptions import ErrorCode, CustomException
         
     | 
| 
      
 10 
     | 
    
         
            +
            from .database import (
         
     | 
| 
      
 11 
     | 
    
         
            +
                list_entities,
         
     | 
| 
      
 12 
     | 
    
         
            +
                get_entity,
         
     | 
| 
      
 13 
     | 
    
         
            +
                create_entity,
         
     | 
| 
      
 14 
     | 
    
         
            +
                update_entity,
         
     | 
| 
      
 15 
     | 
    
         
            +
                delete_entity
         
     | 
| 
      
 16 
     | 
    
         
            +
            )
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            ModelType = TypeVar("ModelType", bound=DeclarativeBase)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            class BaseRepository(Generic[ModelType]):
         
     | 
| 
      
 21 
     | 
    
         
            +
                def __init__(self, session: AsyncSession, model: Type[ModelType]):
         
     | 
| 
      
 22 
     | 
    
         
            +
                    self._session = session
         
     | 
| 
      
 23 
     | 
    
         
            +
                    self.model = model
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                @property
         
     | 
| 
      
 26 
     | 
    
         
            +
                def session(self) -> AsyncSession:
         
     | 
| 
      
 27 
     | 
    
         
            +
                    return self._session
         
     | 
| 
      
 28 
     | 
    
         
            +
                
         
     | 
| 
      
 29 
     | 
    
         
            +
                @session.setter
         
     | 
| 
      
 30 
     | 
    
         
            +
                def session(self, value: AsyncSession):
         
     | 
| 
      
 31 
     | 
    
         
            +
                    if value is None:
         
     | 
| 
      
 32 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 33 
     | 
    
         
            +
                            ErrorCode.DB_CONNECTION_ERROR,
         
     | 
| 
      
 34 
     | 
    
         
            +
                            detail="Session cannot be None",
         
     | 
| 
      
 35 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.session"
         
     | 
| 
      
 36 
     | 
    
         
            +
                        )
         
     | 
| 
      
 37 
     | 
    
         
            +
                    self._session = value
         
     | 
| 
      
 38 
     | 
    
         
            +
               
         
     | 
| 
      
 39 
     | 
    
         
            +
                #######################
         
     | 
| 
      
 40 
     | 
    
         
            +
                # 입력 및 수정, 삭제 #
         
     | 
| 
      
 41 
     | 
    
         
            +
                ####################### 
         
     | 
| 
      
 42 
     | 
    
         
            +
                async def create(
         
     | 
| 
      
 43 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 44 
     | 
    
         
            +
                    entity_data: Dict[str, Any],
         
     | 
| 
      
 45 
     | 
    
         
            +
                    exclude_entities: List[str] | None = None
         
     | 
| 
      
 46 
     | 
    
         
            +
                ) -> ModelType:
         
     | 
| 
      
 47 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 48 
     | 
    
         
            +
                        return await create_entity(
         
     | 
| 
      
 49 
     | 
    
         
            +
                            session=self.session,
         
     | 
| 
      
 50 
     | 
    
         
            +
                            model=self.model,
         
     | 
| 
      
 51 
     | 
    
         
            +
                            entity_data=entity_data,
         
     | 
| 
      
 52 
     | 
    
         
            +
                            exclude_entities=exclude_entities
         
     | 
| 
      
 53 
     | 
    
         
            +
                        )
         
     | 
| 
      
 54 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 55 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 56 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 57 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 58 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 59 
     | 
    
         
            +
                            detail=str(e),
         
     | 
| 
      
 60 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.create",
         
     | 
| 
      
 61 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 62 
     | 
    
         
            +
                        )
         
     | 
| 
      
 63 
     | 
    
         
            +
                    
         
     | 
| 
      
 64 
     | 
    
         
            +
                async def update(
         
     | 
| 
      
 65 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 66 
     | 
    
         
            +
                    update_data: Dict[str, Any],
         
     | 
| 
      
 67 
     | 
    
         
            +
                    conditions: Dict[str, Any]
         
     | 
| 
      
 68 
     | 
    
         
            +
                ) -> ModelType:
         
     | 
| 
      
 69 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 70 
     | 
    
         
            +
                        return await update_entity(
         
     | 
| 
      
 71 
     | 
    
         
            +
                            session=self.session,
         
     | 
| 
      
 72 
     | 
    
         
            +
                            model=self.model,
         
     | 
| 
      
 73 
     | 
    
         
            +
                            update_data=update_data,
         
     | 
| 
      
 74 
     | 
    
         
            +
                            conditions=conditions
         
     | 
| 
      
 75 
     | 
    
         
            +
                        )
         
     | 
| 
      
 76 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 77 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 78 
     | 
    
         
            +
                    
         
     | 
| 
      
 79 
     | 
    
         
            +
                async def delete(
         
     | 
| 
      
 80 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 81 
     | 
    
         
            +
                    conditions: Dict[str, Any]
         
     | 
| 
      
 82 
     | 
    
         
            +
                ) -> None:
         
     | 
| 
      
 83 
     | 
    
         
            +
                    await delete_entity(
         
     | 
| 
      
 84 
     | 
    
         
            +
                        session=self.session,
         
     | 
| 
      
 85 
     | 
    
         
            +
                        model=self.model,
         
     | 
| 
      
 86 
     | 
    
         
            +
                        conditions=conditions
         
     | 
| 
      
 87 
     | 
    
         
            +
                    )
         
     | 
| 
      
 88 
     | 
    
         
            +
                
         
     | 
| 
      
 89 
     | 
    
         
            +
                #########################
         
     | 
| 
      
 90 
     | 
    
         
            +
                # 조회 및 검색 메서드 #
         
     | 
| 
      
 91 
     | 
    
         
            +
                #########################
         
     | 
| 
      
 92 
     | 
    
         
            +
                async def list(
         
     | 
| 
      
 93 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 94 
     | 
    
         
            +
                    skip: int = 0,
         
     | 
| 
      
 95 
     | 
    
         
            +
                    limit: int = 100,
         
     | 
| 
      
 96 
     | 
    
         
            +
                    filters: Optional[Dict[str, Any]] = None,
         
     | 
| 
      
 97 
     | 
    
         
            +
                    explicit_joins: Optional[List[Any]] = None,
         
     | 
| 
      
 98 
     | 
    
         
            +
                    loading_joins: Optional[List[Any]] = None
         
     | 
| 
      
 99 
     | 
    
         
            +
                ) -> List[ModelType]:
         
     | 
| 
      
 100 
     | 
    
         
            +
                    """
         
     | 
| 
      
 101 
     | 
    
         
            +
                    엔티티 목록 조회.
         
     | 
| 
      
 102 
     | 
    
         
            +
                    """
         
     | 
| 
      
 103 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 104 
     | 
    
         
            +
                        # 기본 CRUD 작업 호출
         
     | 
| 
      
 105 
     | 
    
         
            +
                        return await list_entities(
         
     | 
| 
      
 106 
     | 
    
         
            +
                            session=self.session,
         
     | 
| 
      
 107 
     | 
    
         
            +
                            model=self.model,
         
     | 
| 
      
 108 
     | 
    
         
            +
                            skip=skip,
         
     | 
| 
      
 109 
     | 
    
         
            +
                            limit=limit,
         
     | 
| 
      
 110 
     | 
    
         
            +
                            filters=filters,
         
     | 
| 
      
 111 
     | 
    
         
            +
                            explicit_joins=explicit_joins,
         
     | 
| 
      
 112 
     | 
    
         
            +
                            loading_joins=loading_joins
         
     | 
| 
      
 113 
     | 
    
         
            +
                        )
         
     | 
| 
      
 114 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 115 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 116 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 117 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 118 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 119 
     | 
    
         
            +
                            detail=str(e),
         
     | 
| 
      
 120 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.list",
         
     | 
| 
      
 121 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 122 
     | 
    
         
            +
                        )
         
     | 
| 
      
 123 
     | 
    
         
            +
                    
         
     | 
| 
      
 124 
     | 
    
         
            +
                async def get(
         
     | 
| 
      
 125 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 126 
     | 
    
         
            +
                    conditions: Dict[str, Any] | None = None,
         
     | 
| 
      
 127 
     | 
    
         
            +
                    explicit_joins: Optional[List[Any]] = None,
         
     | 
| 
      
 128 
     | 
    
         
            +
                    loading_joins: Optional[List[Any]] = None
         
     | 
| 
      
 129 
     | 
    
         
            +
                ) -> ModelType:
         
     | 
| 
      
 130 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 131 
     | 
    
         
            +
                        return await get_entity(
         
     | 
| 
      
 132 
     | 
    
         
            +
                            session=self.session,
         
     | 
| 
      
 133 
     | 
    
         
            +
                            model=self.model,
         
     | 
| 
      
 134 
     | 
    
         
            +
                            conditions=conditions,
         
     | 
| 
      
 135 
     | 
    
         
            +
                            explicit_joins=explicit_joins,
         
     | 
| 
      
 136 
     | 
    
         
            +
                            loading_joins=loading_joins
         
     | 
| 
      
 137 
     | 
    
         
            +
                        )
         
     | 
| 
      
 138 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 139 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 140 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 141 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 142 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 143 
     | 
    
         
            +
                            detail=str(e),
         
     | 
| 
      
 144 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.get",
         
     | 
| 
      
 145 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 146 
     | 
    
         
            +
                        )
         
     | 
| 
         @@ -0,0 +1,256 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #기본 라이브러리
         
     | 
| 
      
 2 
     | 
    
         
            +
            from fastapi import Request
         
     | 
| 
      
 3 
     | 
    
         
            +
            from typing import TypeVar, Generic, Type, Dict, Any, Union, List, Optional
         
     | 
| 
      
 4 
     | 
    
         
            +
            from sqlalchemy.orm import DeclarativeBase
         
     | 
| 
      
 5 
     | 
    
         
            +
            from sqlalchemy.ext.asyncio import AsyncSession
         
     | 
| 
      
 6 
     | 
    
         
            +
            from datetime import datetime
         
     | 
| 
      
 7 
     | 
    
         
            +
            from ulid import ULID
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            #패키지 라이브러리
         
     | 
| 
      
 10 
     | 
    
         
            +
            from .exceptions import ErrorCode, CustomException
         
     | 
| 
      
 11 
     | 
    
         
            +
            from .base_repository import BaseRepository
         
     | 
| 
      
 12 
     | 
    
         
            +
            from .database import (
         
     | 
| 
      
 13 
     | 
    
         
            +
                process_response,
         
     | 
| 
      
 14 
     | 
    
         
            +
                validate_unique_fields
         
     | 
| 
      
 15 
     | 
    
         
            +
            )
         
     | 
| 
      
 16 
     | 
    
         
            +
            from .security import hash_password
         
     | 
| 
      
 17 
     | 
    
         
            +
            ModelType = TypeVar("ModelType", bound=DeclarativeBase)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            class BaseService(Generic[ModelType]):
         
     | 
| 
      
 20 
     | 
    
         
            +
                ##################
         
     | 
| 
      
 21 
     | 
    
         
            +
                # 1. 초기화 영역 #
         
     | 
| 
      
 22 
     | 
    
         
            +
                ##################
         
     | 
| 
      
 23 
     | 
    
         
            +
                def __init__(
         
     | 
| 
      
 24 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 25 
     | 
    
         
            +
                        model: Type[ModelType],
         
     | 
| 
      
 26 
     | 
    
         
            +
                        repository: BaseRepository[ModelType],
         
     | 
| 
      
 27 
     | 
    
         
            +
                        db_session: AsyncSession,
         
     | 
| 
      
 28 
     | 
    
         
            +
                        additional_models: Dict[str, Type[DeclarativeBase]] = None,
         
     | 
| 
      
 29 
     | 
    
         
            +
                ):
         
     | 
| 
      
 30 
     | 
    
         
            +
                    self.model = model
         
     | 
| 
      
 31 
     | 
    
         
            +
                    self.repository = repository
         
     | 
| 
      
 32 
     | 
    
         
            +
                    self.db_session = db_session
         
     | 
| 
      
 33 
     | 
    
         
            +
                    self.additional_models = additional_models or {},
         
     | 
| 
      
 34 
     | 
    
         
            +
                
         
     | 
| 
      
 35 
     | 
    
         
            +
                #######################
         
     | 
| 
      
 36 
     | 
    
         
            +
                # 입력 및 수정, 삭제 #
         
     | 
| 
      
 37 
     | 
    
         
            +
                #######################
         
     | 
| 
      
 38 
     | 
    
         
            +
                async def create(
         
     | 
| 
      
 39 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 40 
     | 
    
         
            +
                    entity_data: Dict[str, Any],
         
     | 
| 
      
 41 
     | 
    
         
            +
                    model_name: str | None = None,
         
     | 
| 
      
 42 
     | 
    
         
            +
                    response_model: Any = None,
         
     | 
| 
      
 43 
     | 
    
         
            +
                    exclude_entities: List[str] | None = None,
         
     | 
| 
      
 44 
     | 
    
         
            +
                    unique_check: List[Dict[str, Any]] | None = None,
         
     | 
| 
      
 45 
     | 
    
         
            +
                    fk_check: List[Dict[str, Any]] | None = None,
         
     | 
| 
      
 46 
     | 
    
         
            +
                ) -> ModelType:
         
     | 
| 
      
 47 
     | 
    
         
            +
                    
         
     | 
| 
      
 48 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 49 
     | 
    
         
            +
                        async with self.db_session.begin():
         
     | 
| 
      
 50 
     | 
    
         
            +
                            # 고유 검사 수행
         
     | 
| 
      
 51 
     | 
    
         
            +
                            if unique_check:
         
     | 
| 
      
 52 
     | 
    
         
            +
                                await validate_unique_fields(self.db_session, unique_check, find_value=True)
         
     | 
| 
      
 53 
     | 
    
         
            +
                            # 외래 키 검사 수행
         
     | 
| 
      
 54 
     | 
    
         
            +
                            if fk_check:
         
     | 
| 
      
 55 
     | 
    
         
            +
                                await validate_unique_fields(self.db_session, fk_check, find_value=False)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                            # repository의 create 메서드를 트랜잭션 내에서 실행
         
     | 
| 
      
 58 
     | 
    
         
            +
                            return await self.repository.create(
         
     | 
| 
      
 59 
     | 
    
         
            +
                                entity_data=entity_data,
         
     | 
| 
      
 60 
     | 
    
         
            +
                                exclude_entities=exclude_entities
         
     | 
| 
      
 61 
     | 
    
         
            +
                            )
         
     | 
| 
      
 62 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 63 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 64 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 65 
     | 
    
         
            +
                        # 다른 예외 처리
         
     | 
| 
      
 66 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 67 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 68 
     | 
    
         
            +
                            detail=str(e),
         
     | 
| 
      
 69 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.create",
         
     | 
| 
      
 70 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 71 
     | 
    
         
            +
                        )
         
     | 
| 
      
 72 
     | 
    
         
            +
                    
         
     | 
| 
      
 73 
     | 
    
         
            +
                async def update(
         
     | 
| 
      
 74 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 75 
     | 
    
         
            +
                    ulid: str | None = None,
         
     | 
| 
      
 76 
     | 
    
         
            +
                    update_data: Dict[str, Any] | None = None,
         
     | 
| 
      
 77 
     | 
    
         
            +
                    conditions: Dict[str, Any] | None = None,
         
     | 
| 
      
 78 
     | 
    
         
            +
                    unique_check: List[Dict[str, Any]] | None = None,
         
     | 
| 
      
 79 
     | 
    
         
            +
                    exclude_entities: List[str] | None = None
         
     | 
| 
      
 80 
     | 
    
         
            +
                ) -> ModelType:
         
     | 
| 
      
 81 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 82 
     | 
    
         
            +
                        # 고유 검사 수행
         
     | 
| 
      
 83 
     | 
    
         
            +
                        if unique_check:
         
     | 
| 
      
 84 
     | 
    
         
            +
                            await validate_unique_fields(self.db_session, unique_check, find_value=True)
         
     | 
| 
      
 85 
     | 
    
         
            +
                        # 비밀번호가 있으면 해시 처리
         
     | 
| 
      
 86 
     | 
    
         
            +
                        if "password" in update_data:
         
     | 
| 
      
 87 
     | 
    
         
            +
                            update_data["password"] = hash_password(update_data["password"])
         
     | 
| 
      
 88 
     | 
    
         
            +
                        # 제외할 엔티티가 있으면 제외
         
     | 
| 
      
 89 
     | 
    
         
            +
                        if exclude_entities:
         
     | 
| 
      
 90 
     | 
    
         
            +
                            update_data = {k: v for k, v in update_data.items() if k not in exclude_entities}
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                        if not ulid and not conditions:
         
     | 
| 
      
 93 
     | 
    
         
            +
                            raise CustomException(
         
     | 
| 
      
 94 
     | 
    
         
            +
                                ErrorCode.INVALID_INPUT,
         
     | 
| 
      
 95 
     | 
    
         
            +
                                detail="Either 'ulid' or 'conditions' must be provided.",
         
     | 
| 
      
 96 
     | 
    
         
            +
                                source_function="database.update_entity"
         
     | 
| 
      
 97 
     | 
    
         
            +
                            )
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                        # ulid로 조건 생성
         
     | 
| 
      
 100 
     | 
    
         
            +
                        if ulid:
         
     | 
| 
      
 101 
     | 
    
         
            +
                            if not ULID.from_str(ulid):
         
     | 
| 
      
 102 
     | 
    
         
            +
                                raise CustomException(
         
     | 
| 
      
 103 
     | 
    
         
            +
                                    ErrorCode.VALIDATION_ERROR,
         
     | 
| 
      
 104 
     | 
    
         
            +
                                    detail=ulid,
         
     | 
| 
      
 105 
     | 
    
         
            +
                                    source_function=f"{self.__class__.__name__}.update"
         
     | 
| 
      
 106 
     | 
    
         
            +
                                )
         
     | 
| 
      
 107 
     | 
    
         
            +
                            
         
     | 
| 
      
 108 
     | 
    
         
            +
                            conditions = {"ulid": ulid}
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                        return await self.repository.update(
         
     | 
| 
      
 111 
     | 
    
         
            +
                            update_data=update_data,
         
     | 
| 
      
 112 
     | 
    
         
            +
                            conditions=conditions
         
     | 
| 
      
 113 
     | 
    
         
            +
                        )
         
     | 
| 
      
 114 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 115 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 116 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 117 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 118 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 119 
     | 
    
         
            +
                            detail=str(e),
         
     | 
| 
      
 120 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.update",
         
     | 
| 
      
 121 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 122 
     | 
    
         
            +
                        )
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                async def delete(
         
     | 
| 
      
 125 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 126 
     | 
    
         
            +
                    ulid: str | None = None,
         
     | 
| 
      
 127 
     | 
    
         
            +
                    conditions: Dict[str, Any] | None = None
         
     | 
| 
      
 128 
     | 
    
         
            +
                ) -> None:
         
     | 
| 
      
 129 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 130 
     | 
    
         
            +
                        if not ULID.from_str(ulid):
         
     | 
| 
      
 131 
     | 
    
         
            +
                            raise CustomException(
         
     | 
| 
      
 132 
     | 
    
         
            +
                                ErrorCode.VALIDATION_ERROR,
         
     | 
| 
      
 133 
     | 
    
         
            +
                                detail=ulid,
         
     | 
| 
      
 134 
     | 
    
         
            +
                                source_function=f"{self.__class__.__name__}.delete"
         
     | 
| 
      
 135 
     | 
    
         
            +
                            )
         
     | 
| 
      
 136 
     | 
    
         
            +
                        
         
     | 
| 
      
 137 
     | 
    
         
            +
                        if not ulid and not conditions:
         
     | 
| 
      
 138 
     | 
    
         
            +
                            raise CustomException(
         
     | 
| 
      
 139 
     | 
    
         
            +
                                ErrorCode.INVALID_INPUT,
         
     | 
| 
      
 140 
     | 
    
         
            +
                                detail="Either 'ulid' or 'conditions' must be provided.",
         
     | 
| 
      
 141 
     | 
    
         
            +
                                source_function="database.update_entity"
         
     | 
| 
      
 142 
     | 
    
         
            +
                            )
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                        # ulid로 조건 생성
         
     | 
| 
      
 145 
     | 
    
         
            +
                        if ulid:
         
     | 
| 
      
 146 
     | 
    
         
            +
                            conditions = {"ulid": ulid}
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                        conditions["is_deleted"] = False
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                        return await self.repository.delete(
         
     | 
| 
      
 151 
     | 
    
         
            +
                            conditions=conditions
         
     | 
| 
      
 152 
     | 
    
         
            +
                        )
         
     | 
| 
      
 153 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 154 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 155 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 156 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 157 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 158 
     | 
    
         
            +
                            detail=str(e),
         
     | 
| 
      
 159 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.delete",
         
     | 
| 
      
 160 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 161 
     | 
    
         
            +
                        )
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                #########################
         
     | 
| 
      
 164 
     | 
    
         
            +
                # 조회 및 검색 메서드 #
         
     | 
| 
      
 165 
     | 
    
         
            +
                #########################
         
     | 
| 
      
 166 
     | 
    
         
            +
                async def list(
         
     | 
| 
      
 167 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 168 
     | 
    
         
            +
                    skip: int = 0,
         
     | 
| 
      
 169 
     | 
    
         
            +
                    limit: int = 100,
         
     | 
| 
      
 170 
     | 
    
         
            +
                    filters: List[Dict[str, Any]] | None = None,
         
     | 
| 
      
 171 
     | 
    
         
            +
                    model_name: str | None = None,
         
     | 
| 
      
 172 
     | 
    
         
            +
                    response_model: Any = None,
         
     | 
| 
      
 173 
     | 
    
         
            +
                    explicit_joins: Optional[List[Any]] = None,
         
     | 
| 
      
 174 
     | 
    
         
            +
                    loading_joins: Optional[List[Any]] = None
         
     | 
| 
      
 175 
     | 
    
         
            +
                ) -> List[Dict[str, Any]]:
         
     | 
| 
      
 176 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 177 
     | 
    
         
            +
                        # 모델 이름을 통한 동적 처리
         
     | 
| 
      
 178 
     | 
    
         
            +
                        if model_name:
         
     | 
| 
      
 179 
     | 
    
         
            +
                            if model_name not in self.additional_models:
         
     | 
| 
      
 180 
     | 
    
         
            +
                                raise CustomException(
         
     | 
| 
      
 181 
     | 
    
         
            +
                                    ErrorCode.INVALID_REQUEST,
         
     | 
| 
      
 182 
     | 
    
         
            +
                                    detail=f"Model {model_name} not registered",
         
     | 
| 
      
 183 
     | 
    
         
            +
                                    source_function=f"{self.__class__.__name__}.list"
         
     | 
| 
      
 184 
     | 
    
         
            +
                                )
         
     | 
| 
      
 185 
     | 
    
         
            +
                            model = self.additional_models[model_name]
         
     | 
| 
      
 186 
     | 
    
         
            +
                            entities = await self.repository.list(
         
     | 
| 
      
 187 
     | 
    
         
            +
                                skip=skip,
         
     | 
| 
      
 188 
     | 
    
         
            +
                                limit=limit,
         
     | 
| 
      
 189 
     | 
    
         
            +
                                filters=filters,
         
     | 
| 
      
 190 
     | 
    
         
            +
                                model=model
         
     | 
| 
      
 191 
     | 
    
         
            +
                            )
         
     | 
| 
      
 192 
     | 
    
         
            +
                            return [process_response(entity, response_model) for entity in entities]
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                        entities = await self.repository.list(
         
     | 
| 
      
 195 
     | 
    
         
            +
                            skip=skip,
         
     | 
| 
      
 196 
     | 
    
         
            +
                            limit=limit,
         
     | 
| 
      
 197 
     | 
    
         
            +
                            filters=filters,
         
     | 
| 
      
 198 
     | 
    
         
            +
                            explicit_joins=explicit_joins,
         
     | 
| 
      
 199 
     | 
    
         
            +
                            loading_joins=loading_joins
         
     | 
| 
      
 200 
     | 
    
         
            +
                        )
         
     | 
| 
      
 201 
     | 
    
         
            +
                        return [process_response(entity, response_model) for entity in entities]
         
     | 
| 
      
 202 
     | 
    
         
            +
                    
         
     | 
| 
      
 203 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 204 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 205 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 206 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 207 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 208 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.list",
         
     | 
| 
      
 209 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 210 
     | 
    
         
            +
                        )
         
     | 
| 
      
 211 
     | 
    
         
            +
                    
         
     | 
| 
      
 212 
     | 
    
         
            +
                async def get(
         
     | 
| 
      
 213 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 214 
     | 
    
         
            +
                    ulid: str,
         
     | 
| 
      
 215 
     | 
    
         
            +
                    model_name: str | None = None,
         
     | 
| 
      
 216 
     | 
    
         
            +
                    response_model: Any = None,
         
     | 
| 
      
 217 
     | 
    
         
            +
                    conditions: Dict[str, Any] | None = None,
         
     | 
| 
      
 218 
     | 
    
         
            +
                    explicit_joins: Optional[List[Any]] = None,
         
     | 
| 
      
 219 
     | 
    
         
            +
                    loading_joins: Optional[List[Any]] = None
         
     | 
| 
      
 220 
     | 
    
         
            +
                ):
         
     | 
| 
      
 221 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 222 
     | 
    
         
            +
                        if not ulid and not conditions:
         
     | 
| 
      
 223 
     | 
    
         
            +
                            raise CustomException(
         
     | 
| 
      
 224 
     | 
    
         
            +
                                ErrorCode.INVALID_INPUT,
         
     | 
| 
      
 225 
     | 
    
         
            +
                                detail="Either 'ulid' or 'conditions' must be provided.",
         
     | 
| 
      
 226 
     | 
    
         
            +
                                source_function="database.update_entity"
         
     | 
| 
      
 227 
     | 
    
         
            +
                            )
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                        # ulid로 조건 생성
         
     | 
| 
      
 230 
     | 
    
         
            +
                        if ulid:
         
     | 
| 
      
 231 
     | 
    
         
            +
                            if not ULID.from_str(ulid):
         
     | 
| 
      
 232 
     | 
    
         
            +
                                raise CustomException(
         
     | 
| 
      
 233 
     | 
    
         
            +
                                    ErrorCode.VALIDATION_ERROR,
         
     | 
| 
      
 234 
     | 
    
         
            +
                                    detail=ulid,
         
     | 
| 
      
 235 
     | 
    
         
            +
                                    source_function=f"{self.__class__.__name__}.update"
         
     | 
| 
      
 236 
     | 
    
         
            +
                                )
         
     | 
| 
      
 237 
     | 
    
         
            +
                            
         
     | 
| 
      
 238 
     | 
    
         
            +
                            conditions = {"ulid": ulid}
         
     | 
| 
      
 239 
     | 
    
         
            +
                        
         
     | 
| 
      
 240 
     | 
    
         
            +
                        entity = await self.repository.get(
         
     | 
| 
      
 241 
     | 
    
         
            +
                            conditions=conditions,
         
     | 
| 
      
 242 
     | 
    
         
            +
                            explicit_joins=explicit_joins,
         
     | 
| 
      
 243 
     | 
    
         
            +
                            loading_joins=loading_joins
         
     | 
| 
      
 244 
     | 
    
         
            +
                        )
         
     | 
| 
      
 245 
     | 
    
         
            +
                        return process_response(entity, response_model)
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
      
 247 
     | 
    
         
            +
                    except CustomException as e:
         
     | 
| 
      
 248 
     | 
    
         
            +
                        raise e
         
     | 
| 
      
 249 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 250 
     | 
    
         
            +
                        raise CustomException(
         
     | 
| 
      
 251 
     | 
    
         
            +
                            ErrorCode.INTERNAL_ERROR,
         
     | 
| 
      
 252 
     | 
    
         
            +
                            detail=str(e),
         
     | 
| 
      
 253 
     | 
    
         
            +
                            source_function=f"{self.__class__.__name__}.get",
         
     | 
| 
      
 254 
     | 
    
         
            +
                            original_error=e
         
     | 
| 
      
 255 
     | 
    
         
            +
                        )
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     |