aiteamutils 0.2.59__py3-none-any.whl → 0.2.60__py3-none-any.whl

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/__init__.py CHANGED
@@ -21,7 +21,6 @@ from .base_repository import BaseRepository
21
21
  from .validators import validate_with
22
22
  from .enums import ActivityType
23
23
  from .version import __version__
24
- from .dependencies import setup_dependencies, register_service
25
24
 
26
25
  __all__ = [
27
26
  # Base Models
@@ -32,10 +31,6 @@ __all__ = [
32
31
  # Database
33
32
  "DatabaseService",
34
33
 
35
- # Dependencies
36
- "setup_dependencies",
37
- "register_service",
38
-
39
34
  # Exceptions
40
35
  "CustomException",
41
36
  "ErrorCode",
@@ -1,208 +1,54 @@
1
- """기본 레포지토리 모듈."""
2
- from typing import TypeVar, Generic, Dict, Any, List, Optional, Type, Union
3
- from sqlalchemy.orm import DeclarativeBase, Load
4
- from sqlalchemy.exc import IntegrityError, SQLAlchemyError
5
- from sqlalchemy import select, or_, and_
6
- from .exceptions import CustomException, ErrorCode
7
- from sqlalchemy.orm import joinedload
8
- from sqlalchemy.sql import Select
9
- from fastapi import Request
1
+ #기본 라이브러리
2
+ from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
3
+ from sqlalchemy.orm import DeclarativeBase
10
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 list_entities
11
11
 
12
12
  ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
13
 
14
14
  class BaseRepository(Generic[ModelType]):
15
- ##################
16
- # 1. 초기화 영역 #
17
- ##################
18
15
  def __init__(self, session: AsyncSession, model: Type[ModelType]):
19
- """
20
- Args:
21
- session (AsyncSession): 데이터베이스 세션
22
- model (Type[ModelType]): 모델 클래스
23
- """
24
- self.session = session
25
- self.model = model
26
-
27
- @property
28
- def session(self):
29
- """현재 세션을 반환합니다."""
30
- if self._session is None:
16
+ if session is None:
31
17
  raise CustomException(
32
18
  ErrorCode.DB_CONNECTION_ERROR,
33
19
  detail="Database session is not set",
34
20
  source_function=f"{self.__class__.__name__}.session"
35
21
  )
36
- return self._session
37
-
38
- @session.setter
39
- def session(self, value):
40
- """세션을 설정합니다."""
41
- self._session = value
42
-
43
- #######################
44
- # 2. CRUD 작업 #
45
- #######################
46
- async def get(
47
- self,
48
- ulid: str
49
- ) -> Optional[Dict[str, Any]]:
50
- """ULID로 엔티티를 조회합니다."""
51
- try:
52
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
53
- result = await self.session.execute(stmt)
54
- entity = result.scalars().unique().first()
55
-
56
- if not entity:
57
- raise CustomException(
58
- ErrorCode.DB_NO_RESULT,
59
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
60
- source_function=f"{self.__class__.__name__}.get"
61
- )
62
- return entity
63
- except CustomException as e:
64
- e.detail = f"Repository error for {self.model.__tablename__}: {e.detail}"
65
- e.source_function = f"{self.__class__.__name__}.get -> {e.source_function}"
66
- raise e
67
- except SQLAlchemyError as e:
22
+ if model is None:
68
23
  raise CustomException(
69
- ErrorCode.DB_QUERY_ERROR,
70
- detail=f"Database error in {self.model.__tablename__}: {str(e)}",
71
- source_function=f"{self.__class__.__name__}.get",
72
- original_error=e
24
+ ErrorCode.DB_CONNECTION_ERROR,
25
+ detail="Model is not set",
26
+ source_function=f"{self.__class__.__name__}.model"
73
27
  )
74
28
 
29
+ self.session = session
30
+ self.model = model
31
+
32
+ @property
33
+ def session(self) -> AsyncSession:
34
+ return self._session
35
+
75
36
  async def list(
76
37
  self,
77
38
  skip: int = 0,
78
39
  limit: int = 100,
79
- filters: Dict[str, Any] | None = None,
80
- search_params: Dict[str, Any] | None = None
81
- ) -> List[Any]:
82
- """엔티티 목록을 조회합니다."""
83
- try:
84
- stmt = select(self.model).where(self.model.is_deleted == False)
85
-
86
- # 필터 적용
87
- if filters:
88
- stmt = self._apply_filters(stmt, filters)
89
-
90
- # 검색 적용
91
- if search_params:
92
- stmt = self._apply_search_params(stmt, search_params)
93
-
94
- # 페이지네이션 적용
95
- stmt = stmt.limit(limit).offset(skip)
96
-
97
- result = await self.session.execute(stmt)
98
- return result.scalars().unique().all()
99
-
100
- except SQLAlchemyError as e:
101
- raise CustomException(
102
- ErrorCode.DB_QUERY_ERROR,
103
- detail=f"Unexpected repository list error in {self.model.__tablename__}: {str(e)}",
104
- source_function=f"{self.__class__.__name__}.list",
105
- original_error=e,
106
- )
107
-
108
- async def create(self, data: Dict[str, Any]) -> ModelType:
109
- """새로운 엔티티를 생성합니다."""
110
- try:
111
- entity = self.model(**data)
112
- self.session.add(entity)
113
- await self.session.flush()
114
- await self.session.refresh(entity)
115
- return entity
116
- except IntegrityError as e:
117
- await self.session.rollback()
118
- self._handle_integrity_error(e, "create", data)
119
- except SQLAlchemyError as e:
120
- await self.session.rollback()
121
- raise CustomException(
122
- ErrorCode.DB_CREATE_ERROR,
123
- detail=f"Database create error in {self.model.__tablename__}: {str(e)}",
124
- source_function=f"{self.__class__.__name__}.create",
125
- original_error=e
126
- )
127
-
128
- async def update(self, ulid: str, data: Dict[str, Any]) -> Optional[ModelType]:
129
- """기존 엔티티를 수정합니다."""
130
- try:
131
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
132
- result = await self.session.execute(stmt)
133
- entity = result.scalars().first()
134
-
135
- if not entity:
136
- raise CustomException(
137
- ErrorCode.DB_NO_RESULT,
138
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
139
- source_function=f"{self.__class__.__name__}.update"
140
- )
141
-
142
- for key, value in data.items():
143
- setattr(entity, key, value)
144
-
145
- await self.session.flush()
146
- await self.session.refresh(entity)
147
- return entity
148
-
149
- except IntegrityError as e:
150
- await self.session.rollback()
151
- self._handle_integrity_error(e, "update", data)
152
- except SQLAlchemyError as e:
153
- await self.session.rollback()
154
- raise CustomException(
155
- ErrorCode.DB_UPDATE_ERROR,
156
- detail=f"Database update error in {self.model.__tablename__}: {str(e)}",
157
- source_function=f"{self.__class__.__name__}.update",
158
- original_error=e
159
- )
160
-
161
- async def delete(self, ulid: str) -> bool:
162
- """엔티티를 소프트 삭제합니다."""
163
- try:
164
- stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
165
- result = await self.session.execute(stmt)
166
- entity = result.scalars().first()
167
-
168
- if not entity:
169
- raise CustomException(
170
- ErrorCode.DB_NO_RESULT,
171
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
172
- source_function=f"{self.__class__.__name__}.delete"
173
- )
174
-
175
- entity.is_deleted = True
176
- await self.session.flush()
177
- return True
178
-
179
- except SQLAlchemyError as e:
180
- await self.session.rollback()
181
- raise CustomException(
182
- ErrorCode.DB_DELETE_ERROR,
183
- detail=f"Database delete error in {self.model.__tablename__}: {str(e)}",
184
- source_function=f"{self.__class__.__name__}.delete",
185
- original_error=e
186
- )
187
-
188
- async def real_delete(self, ulid: str) -> bool:
189
- """엔티티를 실제로 삭제합니다."""
190
- try:
191
- stmt = select(self.model).filter_by(ulid=ulid)
192
- result = await self.session.execute(stmt)
193
- entity = result.scalars().first()
194
-
195
- if entity:
196
- await self.session.delete(entity)
197
- await self.session.flush()
198
- return True
199
- return False
200
-
201
- except SQLAlchemyError as e:
202
- await self.session.rollback()
203
- raise CustomException(
204
- ErrorCode.DB_DELETE_ERROR,
205
- detail=f"Database real delete error in {self.model.__tablename__}: {str(e)}",
206
- source_function=f"{self.__class__.__name__}.real_delete",
207
- original_error=e
208
- )
40
+ filters: Optional[Dict[str, Any]] = None,
41
+ joins: Optional[List[Any]] = None
42
+ ) -> List[ModelType]:
43
+ """
44
+ 엔티티 목록 조회.
45
+ """
46
+ # 기본 CRUD 작업 호출
47
+ return await list_entities(
48
+ session=self.db_session,
49
+ model=self.model,
50
+ skip=skip,
51
+ limit=limit,
52
+ filters=filters,
53
+ joins=joins,
54
+ )
@@ -1,260 +1,60 @@
1
- """기본 서비스 모듈."""
2
- from datetime import datetime
3
- from typing import TypeVar, Generic, Dict, Any, List, Optional, Type
4
- from sqlalchemy.ext.asyncio import AsyncSession
5
- from sqlalchemy.orm import DeclarativeBase
1
+ #기본 라이브러리
6
2
  from fastapi import Request
7
-
8
- from .database import DatabaseService
9
- from .exceptions import CustomException, ErrorCode
3
+ from typing import TypeVar, Generic, Type, Dict, Any, Union, List
4
+ from sqlalchemy.orm import DeclarativeBase
5
+ from sqlalchemy.ext.asyncio import AsyncSession
6
+ from datetime import datetime
7
+ #패키지 라이브러리
8
+ from .exceptions import ErrorCode, CustomException
10
9
  from .base_repository import BaseRepository
11
10
 
12
11
  ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
12
 
14
13
  class BaseService(Generic[ModelType]):
14
+ ##################
15
+ # 1. 초기화 영역 #
16
+ ##################
15
17
  def __init__(
16
- self,
17
- db: DatabaseService,
18
- repository: Optional[BaseRepository] = None,
19
- request: Optional[Request] = None
18
+ self,
19
+ model: Type[ModelType],
20
+ repository: BaseRepository[ModelType],
21
+ db_session: AsyncSession
20
22
  ):
21
- """BaseService 초기화
22
-
23
- Args:
24
- db: 데이터베이스 서비스
25
- repository: 레포지토리 인스턴스 (선택)
26
- request: FastAPI 요청 객체 (선택)
27
- """
28
- self.db = db
23
+ self.model = model
29
24
  self.repository = repository
30
- self.request = request
31
- self.model = repository.model if repository else None
32
-
33
- def _process_response(self, entity: ModelType, response_model: Any = None) -> Dict[str, Any]:
34
- """응답 데이터를 처리합니다.
35
-
36
- Args:
37
- entity: 처리할 엔티티
38
- response_model: 응답 모델 클래스
39
-
40
- Returns:
41
- 처리된 데이터
42
- """
43
- if not entity:
44
- return None
45
-
46
- # 기본 데이터 변환
47
- result = {}
48
-
49
- # 테이블 컬럼 처리
50
- for column in entity.__table__.columns:
51
- value = getattr(entity, column.name)
52
- if isinstance(value, datetime):
53
- value = value.isoformat()
54
- result[column.name] = value
55
-
56
- # Relationship 처리 (이미 로드된 관계만)
57
- for relationship in entity.__mapper__.relationships:
58
- if relationship.key not in entity.__dict__:
59
- continue
60
-
61
- try:
62
- value = getattr(entity, relationship.key)
63
- if value is not None:
64
- if isinstance(value, list):
65
- result[relationship.key] = [
66
- self._process_response(item)
67
- for item in value
68
- ]
69
- else:
70
- result[relationship.key] = self._process_response(value)
71
- else:
72
- result[relationship.key] = None
73
- except Exception:
74
- result[relationship.key] = None
75
-
76
- # response_model이 있는 경우 필터링
77
- if response_model:
78
- # response_model에 없는 필드 제거
79
- keys_to_remove = [key for key in result if key not in response_model.model_fields]
80
- for key in keys_to_remove:
81
- result.pop(key)
82
- # 모델 검증
83
- return response_model(**result).model_dump()
84
-
85
- return result
86
-
87
- async def create(
88
- self,
89
- data: Dict[str, Any],
90
- response_model: Any = None
91
- ) -> Dict[str, Any]:
92
- """엔티티를 생성합니다.
93
-
94
- Args:
95
- data: 생성할 데이터
96
- response_model: 응답 모델 클래스
97
-
98
- Returns:
99
- 생성된 엔티티
100
-
101
- Raises:
102
- CustomException: 생성 실패 시
103
- """
104
- try:
105
- entity = await self.db.create_entity(self.model, data)
106
- return self._process_response(entity, response_model)
107
- except CustomException as e:
108
- raise e
109
- except Exception as e:
110
- raise CustomException(
111
- ErrorCode.DB_CREATE_ERROR,
112
- detail=str(e),
113
- source_function=f"{self.__class__.__name__}.create",
114
- original_error=e
115
- )
116
-
117
- async def get(
118
- self,
119
- ulid: str,
120
- response_model: Any = None
121
- ) -> Dict[str, Any]:
122
- """엔티티를 조회합니다.
123
-
124
- Args:
125
- ulid: 조회할 엔티티의 ULID
126
- response_model: 응답 모델 클래스
127
-
128
- Returns:
129
- 조회된 엔티티
130
-
131
- Raises:
132
- CustomException: 조회 실패 시
133
- """
134
- try:
135
- entity = await self.db.get_entity(self.model, {"ulid": ulid, "is_deleted": False})
136
- if not entity:
137
- raise CustomException(
138
- ErrorCode.NOT_FOUND,
139
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
140
- source_function=f"{self.__class__.__name__}.get"
141
- )
142
- return self._process_response(entity, response_model)
143
- except CustomException as e:
144
- raise e
145
- except Exception as e:
146
- raise CustomException(
147
- ErrorCode.DB_QUERY_ERROR,
148
- detail=str(e),
149
- source_function=f"{self.__class__.__name__}.get",
150
- original_error=e
151
- )
25
+ self.db_session = db_session
152
26
 
153
27
  async def list(
154
28
  self,
155
29
  skip: int = 0,
156
30
  limit: int = 100,
157
- filters: Optional[Dict[str, Any]] = None,
158
- response_model: Any = None
31
+ filters: Dict[str, Any] | None = None,
32
+ search_params: Dict[str, Any] | None = None,
33
+ model_name: str | None = None,
159
34
  ) -> List[Dict[str, Any]]:
160
- """엔티티 목록을 조회합니다.
161
-
162
- Args:
163
- skip: 건너뛸 레코드 수
164
- limit: 조회할 최대 레코드 수
165
- filters: 필터 조건
166
- response_model: 응답 모델 클래스
167
-
168
- Returns:
169
- 엔티티 목록
170
-
171
- Raises:
172
- CustomException: 조회 실패 시
173
- """
174
35
  try:
175
- entities = await self.db.list_entities(
176
- self.model,
177
- filters=filters,
178
- skip=skip,
179
- limit=limit
180
- )
181
- return [self._process_response(entity, response_model) for entity in entities]
36
+ # 모델 이름을 통한 동적 처리
37
+ if model_name:
38
+ if model_name not in self.additional_models:
39
+ raise CustomException(
40
+ ErrorCode.INVALID_REQUEST,
41
+ detail=f"Model {model_name} not registered",
42
+ source_function=f"{self.__class__.__name__}.list"
43
+ )
44
+ model = self.additional_models[model_name]
45
+ return await self.repository.list(skip=skip, limit=limit, filters=filters, model=model)
46
+
47
+ return await self.repository.list(skip=skip, limit=limit, filters=filters)
182
48
  except CustomException as e:
49
+ e.detail = f"Service list error for {self.repository.model.__tablename__}: {e.detail}"
50
+ e.source_function = f"{self.__class__.__name__}.list -> {e.source_function}"
183
51
  raise e
184
52
  except Exception as e:
185
53
  raise CustomException(
186
- ErrorCode.DB_QUERY_ERROR,
54
+ ErrorCode.INTERNAL_ERROR,
187
55
  detail=str(e),
188
56
  source_function=f"{self.__class__.__name__}.list",
189
57
  original_error=e
190
58
  )
191
59
 
192
- async def update(
193
- self,
194
- ulid: str,
195
- data: Dict[str, Any],
196
- response_model: Any = None
197
- ) -> Dict[str, Any]:
198
- """엔티티를 수정합니다.
199
-
200
- Args:
201
- ulid: 수정할 엔티티의 ULID
202
- data: 수정할 데이터
203
- response_model: 응답 모델 클래스
204
-
205
- Returns:
206
- 수정된 엔티티
207
-
208
- Raises:
209
- CustomException: 수정 실패 시
210
- """
211
- try:
212
- entity = await self.db.get_entity(self.model, {"ulid": ulid, "is_deleted": False})
213
- if not entity:
214
- raise CustomException(
215
- ErrorCode.NOT_FOUND,
216
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
217
- source_function=f"{self.__class__.__name__}.update"
218
- )
219
- updated = await self.db.update_entity(entity, data)
220
- return self._process_response(updated, response_model)
221
- except CustomException as e:
222
- raise e
223
- except Exception as e:
224
- raise CustomException(
225
- ErrorCode.DB_UPDATE_ERROR,
226
- detail=str(e),
227
- source_function=f"{self.__class__.__name__}.update",
228
- original_error=e
229
- )
230
60
 
231
- async def delete(self, ulid: str) -> bool:
232
- """엔티티를 삭제합니다.
233
-
234
- Args:
235
- ulid: 삭제할 엔티티의 ULID
236
-
237
- Returns:
238
- 삭제 성공 여부
239
-
240
- Raises:
241
- CustomException: 삭제 실패 시
242
- """
243
- try:
244
- entity = await self.db.get_entity(self.model, {"ulid": ulid, "is_deleted": False})
245
- if not entity:
246
- raise CustomException(
247
- ErrorCode.NOT_FOUND,
248
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
249
- source_function=f"{self.__class__.__name__}.delete"
250
- )
251
- return await self.db.delete_entity(entity)
252
- except CustomException as e:
253
- raise e
254
- except Exception as e:
255
- raise CustomException(
256
- ErrorCode.DB_DELETE_ERROR,
257
- detail=str(e),
258
- source_function=f"{self.__class__.__name__}.delete",
259
- original_error=e
260
- )
aiteamutils/database.py CHANGED
@@ -1,207 +1,48 @@
1
- """데이터베이스 유틸리티 모듈."""
2
- from typing import Any, Dict, Optional, Type, List, Union
3
- from sqlalchemy import select, and_
1
+ #기본 라이브러리
2
+ from typing import TypeVar, Generic, Type, Any, Dict, List, Optional
4
3
  from sqlalchemy.ext.asyncio import AsyncSession
5
- from sqlalchemy.exc import IntegrityError, SQLAlchemyError
4
+ from sqlalchemy import select, and_
5
+ from sqlalchemy.orm import DeclarativeBase
6
+ from sqlalchemy.exc import SQLAlchemyError
6
7
 
7
- from .exceptions import ErrorCode, CustomException
8
- from .base_model import Base
8
+ ModelType = TypeVar("ModelType", bound=DeclarativeBase)
9
9
 
10
- class DatabaseService:
11
- def __init__(self, session: AsyncSession):
12
- """DatabaseService 초기화
13
-
14
- Args:
15
- session (AsyncSession): 외부에서 주입받은 데이터베이스 세션
16
- """
17
- self._session = session
18
-
19
- @property
20
- def session(self) -> AsyncSession:
21
- """현재 세션을 반환합니다."""
22
- if self._session is None:
23
- raise CustomException(
24
- ErrorCode.DB_CONNECTION_ERROR,
25
- detail="session",
26
- source_function="DatabaseService.session"
27
- )
28
- return self._session
10
+ #패키지 라이브러리
11
+ from .exceptions import ErrorCode, CustomException
29
12
 
30
- async def create_entity(
31
- self,
32
- model: Type[Base],
33
- entity_data: Dict[str, Any]
34
- ) -> Any:
35
- """엔티티를 생성합니다.
36
-
37
- Args:
38
- model: 모델 클래스
39
- entity_data: 생성할 엔티티 데이터
40
-
41
- Returns:
42
- 생성된 엔티티
43
-
44
- Raises:
45
- CustomException: 엔티티 생성 실패 시
46
- """
47
- try:
48
- entity = model(**entity_data)
49
- self.session.add(entity)
50
- await self.session.flush()
51
- await self.session.refresh(entity)
52
- return entity
53
- except IntegrityError as e:
54
- await self.session.rollback()
55
- raise CustomException(
56
- ErrorCode.DB_INTEGRITY_ERROR,
57
- detail=str(e),
58
- source_function="DatabaseService.create_entity",
59
- original_error=e
60
- )
61
- except Exception as e:
62
- await self.session.rollback()
63
- raise CustomException(
64
- ErrorCode.DB_CREATE_ERROR,
65
- detail=str(e),
66
- source_function="DatabaseService.create_entity",
67
- original_error=e
68
- )
13
+ ##################
14
+ # 1. 쿼리 실행 #
15
+ ##################
16
+ async def list_entities(
17
+ session: AsyncSession,
18
+ model: Type[ModelType],
19
+ skip: int = 0,
20
+ limit: int = 100,
21
+ filters: Optional[Dict[str, Any]] = None,
22
+ joins: Optional[List[Any]] = None
23
+ ) -> List[Dict[str, Any]]:
24
+ try:
25
+ query = select(model)
69
26
 
70
- async def get_entity(
71
- self,
72
- model: Type[Base],
73
- filters: Dict[str, Any]
74
- ) -> Optional[Any]:
75
- """필터 조건으로 엔티티를 조회합니다.
76
-
77
- Args:
78
- model: 모델 클래스
79
- filters: 필터 조건
80
-
81
- Returns:
82
- 조회된 엔티티 또는 None
83
-
84
- Raises:
85
- CustomException: 조회 실패 시
86
- """
87
- try:
88
- stmt = select(model).filter_by(**filters)
89
- result = await self.session.execute(stmt)
90
- return result.scalars().first()
91
- except Exception as e:
92
- raise CustomException(
93
- ErrorCode.DB_QUERY_ERROR,
94
- detail=str(e),
95
- source_function="DatabaseService.get_entity",
96
- original_error=e
97
- )
27
+ # 필터 조건 적용
28
+ if filters:
29
+ conditions = [getattr(model, key) == value for key, value in filters.items()]
30
+ query = query.where(and_(*conditions))
98
31
 
99
- async def list_entities(
100
- self,
101
- model: Type[Base],
102
- filters: Optional[Dict[str, Any]] = None,
103
- skip: int = 0,
104
- limit: int = 100
105
- ) -> List[Any]:
106
- """엔티티 목록을 조회합니다.
107
-
108
- Args:
109
- model: 모델 클래스
110
- filters: 필터 조건
111
- skip: 건너뛸 레코드 수
112
- limit: 조회할 최대 레코드 수
113
-
114
- Returns:
115
- 엔티티 목록
116
-
117
- Raises:
118
- CustomException: 조회 실패 시
119
- """
120
- try:
121
- stmt = select(model)
122
- if filters:
123
- stmt = stmt.filter_by(**filters)
124
- stmt = stmt.offset(skip).limit(limit)
125
- result = await self.session.execute(stmt)
126
- return result.scalars().all()
127
- except Exception as e:
128
- raise CustomException(
129
- ErrorCode.DB_QUERY_ERROR,
130
- detail=str(e),
131
- source_function="DatabaseService.list_entities",
132
- original_error=e
133
- )
32
+ # 조인 로딩 적용
33
+ if joins:
34
+ for join_option in joins:
35
+ query = query.options(join_option)
134
36
 
135
- async def update_entity(
136
- self,
137
- entity: Base,
138
- update_data: Dict[str, Any]
139
- ) -> Any:
140
- """엔티티를 수정합니다.
141
-
142
- Args:
143
- entity: 수정할 엔티티
144
- update_data: 수정할 데이터
145
-
146
- Returns:
147
- 수정된 엔티티
148
-
149
- Raises:
150
- CustomException: 수정 실패 시
151
- """
152
- try:
153
- for key, value in update_data.items():
154
- setattr(entity, key, value)
155
- await self.session.flush()
156
- await self.session.refresh(entity)
157
- return entity
158
- except IntegrityError as e:
159
- await self.session.rollback()
160
- raise CustomException(
161
- ErrorCode.DB_INTEGRITY_ERROR,
162
- detail=str(e),
163
- source_function="DatabaseService.update_entity",
164
- original_error=e
165
- )
166
- except Exception as e:
167
- await self.session.rollback()
168
- raise CustomException(
169
- ErrorCode.DB_UPDATE_ERROR,
170
- detail=str(e),
171
- source_function="DatabaseService.update_entity",
172
- original_error=e
173
- )
37
+ # 페이지네이션 적용
38
+ query = query.limit(limit).offset(skip)
174
39
 
175
- async def delete_entity(
176
- self,
177
- entity: Base,
178
- soft_delete: bool = True
179
- ) -> bool:
180
- """엔티티를 삭제합니다.
181
-
182
- Args:
183
- entity: 삭제할 엔티티
184
- soft_delete: 소프트 삭제 여부
185
-
186
- Returns:
187
- 삭제 성공 여부
188
-
189
- Raises:
190
- CustomException: 삭제 실패 시
191
- """
192
- try:
193
- if soft_delete:
194
- entity.is_deleted = True
195
- await self.session.flush()
196
- else:
197
- await self.session.delete(entity)
198
- await self.session.flush()
199
- return True
200
- except Exception as e:
201
- await self.session.rollback()
202
- raise CustomException(
203
- ErrorCode.DB_DELETE_ERROR,
204
- detail=str(e),
205
- source_function="DatabaseService.delete_entity",
206
- original_error=e
207
- )
40
+ result = await session.execute(query)
41
+ return result.scalars().unique().all()
42
+ except SQLAlchemyError as e:
43
+ raise CustomException(
44
+ ErrorCode.DB_READ_ERROR,
45
+ detail=f"{model.__name__}|{str(e)}",
46
+ source_function="database.list_entities",
47
+ original_error=e
48
+ )
aiteamutils/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """버전 정보"""
2
- __version__ = "0.2.59"
2
+ __version__ = "0.2.60"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiteamutils
3
- Version: 0.2.59
3
+ Version: 0.2.60
4
4
  Summary: AI Team Utilities
5
5
  Project-URL: Homepage, https://github.com/yourusername/aiteamutils
6
6
  Project-URL: Issues, https://github.com/yourusername/aiteamutils/issues
@@ -0,0 +1,15 @@
1
+ aiteamutils/__init__.py,sha256=h7iFifWvRlaComsk4VPapRr16YhD19Kp1LuBzLHqdgQ,1170
2
+ aiteamutils/base_model.py,sha256=ODEnjvUVoxQ1RPCfq8-uZTfTADIA4c7Z3E6G4EVsSX0,2708
3
+ aiteamutils/base_repository.py,sha256=ir2ftophKKd_9BQ9B0LbszMqUyFeWkQ1MmFF6WBLcWc,1682
4
+ aiteamutils/base_service.py,sha256=Ncomi5yBUL4oHrMx_J1yUKVrUIYo6YmnYJWAWOIwQx8,2217
5
+ aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
6
+ aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
7
+ aiteamutils/database.py,sha256=MtmrX_pDzKFQM-P3OAfm2mALvhRg-v5JWtGBoiegeU0,1484
8
+ aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
9
+ aiteamutils/exceptions.py,sha256=_lKWXq_ujNj41xN6LDE149PwsecAP7lgYWbOBbLOntg,15368
10
+ aiteamutils/security.py,sha256=xFVrjttxwXB1TTjqgRQQgQJQohQBT28vuW8FVLjvi-M,10103
11
+ aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
12
+ aiteamutils/version.py,sha256=W0y4-03EvxLX9F9qSLA2iCovEsiyhT86gHqCOTRNhGU,42
13
+ aiteamutils-0.2.60.dist-info/METADATA,sha256=Gu1bs923MYkC_xKFhMKgS8PrSsK2ne2UJRUxBBeflRI,1718
14
+ aiteamutils-0.2.60.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ aiteamutils-0.2.60.dist-info/RECORD,,
@@ -1,138 +0,0 @@
1
- """의존성 관리 모듈."""
2
- from typing import Type, TypeVar, Dict, Any, Optional, Callable, List, AsyncGenerator
3
- from fastapi import Request, Depends
4
- from sqlalchemy.ext.asyncio import AsyncSession
5
-
6
- from .exceptions import CustomException, ErrorCode
7
- from .base_service import BaseService
8
- from .base_repository import BaseRepository
9
- from .database import DatabaseService
10
-
11
- T = TypeVar("T", bound=BaseService)
12
- R = TypeVar("R", bound=BaseRepository)
13
-
14
- _service_registry: Dict[str, Dict[str, Any]] = {}
15
- _session_provider = None
16
-
17
- __all__ = [
18
- "setup_dependencies",
19
- "register_service",
20
- "get_service"
21
- ]
22
-
23
- def setup_dependencies(session_provider: Callable[[], AsyncGenerator[AsyncSession, None]]) -> None:
24
- """의존성 설정을 초기화합니다.
25
-
26
- Args:
27
- session_provider: 데이터베이스 세션을 제공하는 함수
28
- """
29
- global _session_provider
30
- _session_provider = session_provider
31
-
32
- def register_service(
33
- service_class: Type[T],
34
- repository_class: Optional[Type[R]] = None,
35
- **kwargs
36
- ) -> None:
37
- """서비스를 등록합니다.
38
-
39
- Args:
40
- service_class: 서비스 클래스
41
- repository_class: 저장소 클래스 (선택)
42
- **kwargs: 추가 의존성
43
- """
44
- if _session_provider is None:
45
- raise CustomException(
46
- ErrorCode.INTERNAL_ERROR,
47
- detail="Dependencies not initialized",
48
- source_function="dependencies.register_service"
49
- )
50
-
51
- service_name = service_class.__name__
52
- _service_registry[service_name] = {
53
- "service_class": service_class,
54
- "repository_class": repository_class,
55
- "dependencies": kwargs
56
- }
57
-
58
- async def _get_service(
59
- service_name: str,
60
- session: AsyncSession,
61
- request: Request
62
- ) -> BaseService:
63
- """서비스 인스턴스를 생성합니다.
64
-
65
- Args:
66
- service_name: 서비스 이름
67
- session: 데이터베이스 세션
68
- request: FastAPI 요청 객체
69
-
70
- Returns:
71
- BaseService: 서비스 인스턴스
72
-
73
- Raises:
74
- CustomException: 서비스 생성 실패 시
75
- """
76
- try:
77
- service_info = _service_registry.get(service_name)
78
- if not service_info:
79
- raise CustomException(
80
- ErrorCode.SERVICE_NOT_FOUND,
81
- detail=service_name,
82
- source_function="dependencies._get_service"
83
- )
84
-
85
- service_class = service_info["service_class"]
86
- repository_class = service_info["repository_class"]
87
- dependencies = service_info["dependencies"]
88
-
89
- # 데이터베이스 서비스 생성
90
- db_service = DatabaseService(session=session)
91
-
92
- # 저장소 인스턴스 생성
93
- repository = None
94
- if repository_class:
95
- repository = repository_class(session=session)
96
-
97
- # 서비스 인스턴스 생성
98
- service = service_class(
99
- db=db_service,
100
- repository=repository,
101
- request=request,
102
- **dependencies
103
- )
104
-
105
- return service
106
-
107
- except CustomException as e:
108
- raise e
109
- except Exception as e:
110
- raise CustomException(
111
- ErrorCode.INTERNAL_ERROR,
112
- detail=str(e),
113
- source_function="dependencies._get_service",
114
- original_error=e
115
- )
116
-
117
- def get_service(service_name: str) -> Callable:
118
- """서비스 의존성을 반환합니다.
119
-
120
- Args:
121
- service_name: 서비스 이름
122
-
123
- Returns:
124
- Callable: 서비스 의존성 함수
125
- """
126
- if _session_provider is None:
127
- raise CustomException(
128
- ErrorCode.INTERNAL_ERROR,
129
- detail="Dependencies not initialized",
130
- source_function="dependencies.get_service"
131
- )
132
-
133
- async def _get_service_dependency(
134
- request: Request,
135
- session: AsyncSession = Depends(_session_provider)
136
- ) -> BaseService:
137
- return await _get_service(service_name, session, request)
138
- return _get_service_dependency
@@ -1,16 +0,0 @@
1
- aiteamutils/__init__.py,sha256=U9UEyU0AqpLZzbPJiBvwDEh-s2405y1IeWZ0zCqFSl0,1307
2
- aiteamutils/base_model.py,sha256=ODEnjvUVoxQ1RPCfq8-uZTfTADIA4c7Z3E6G4EVsSX0,2708
3
- aiteamutils/base_repository.py,sha256=vqsundoN0h7FVvgqTBEnnJNMcFpvMK0s_nxBWdIYg-U,7846
4
- aiteamutils/base_service.py,sha256=o4oeX__RmUFp5kV-fb4VwBhF8nTAZNMjoWJvK7G1Wmk,8615
5
- aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
6
- aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
7
- aiteamutils/database.py,sha256=x0x5gnSyGfwo_klL9O65RnGOQID6c9tH2miwFveVyoE,6326
8
- aiteamutils/dependencies.py,sha256=Qoy_M7r65Fv3Y8RMVBi81QLdfjfWIk-pm7O5_Przsa4,4084
9
- aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
10
- aiteamutils/exceptions.py,sha256=_lKWXq_ujNj41xN6LDE149PwsecAP7lgYWbOBbLOntg,15368
11
- aiteamutils/security.py,sha256=xFVrjttxwXB1TTjqgRQQgQJQohQBT28vuW8FVLjvi-M,10103
12
- aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
13
- aiteamutils/version.py,sha256=_ql6JzqtHA14js-9_8pUBJDK_7BZm5uHqA1j4jfryCs,42
14
- aiteamutils-0.2.59.dist-info/METADATA,sha256=OOT0-1lTWlUQv7fL2MtELHAUPSj18nqiaiXRyFc8QoQ,1718
15
- aiteamutils-0.2.59.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- aiteamutils-0.2.59.dist-info/RECORD,,