aiteamutils 0.2.51__py3-none-any.whl → 0.2.53__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.
@@ -3,281 +3,54 @@ from typing import TypeVar, Generic, Dict, Any, List, Optional, Type, Union
3
3
  from sqlalchemy.orm import DeclarativeBase, Load
4
4
  from sqlalchemy.exc import IntegrityError, SQLAlchemyError
5
5
  from sqlalchemy import select, or_, and_
6
- from .database import DatabaseService
7
6
  from .exceptions import CustomException, ErrorCode
8
7
  from sqlalchemy.orm import joinedload
9
8
  from sqlalchemy.sql import Select
10
9
  from fastapi import Request
10
+ from sqlalchemy.ext.asyncio import AsyncSession
11
11
 
12
12
  ModelType = TypeVar("ModelType", bound=DeclarativeBase)
13
13
 
14
14
  class BaseRepository(Generic[ModelType]):
15
- ##################
15
+ ##################
16
16
  # 1. 초기화 영역 #
17
17
  ##################
18
- def __init__(self, db_service: DatabaseService, model: Type[ModelType]):
18
+ def __init__(self, session: AsyncSession, model: Type[ModelType]):
19
19
  """
20
20
  Args:
21
- db_service (DatabaseService): 데이터베이스 서비스 인스턴스
21
+ session (AsyncSession): 데이터베이스 세션
22
22
  model (Type[ModelType]): 모델 클래스
23
23
  """
24
- self.db_service = db_service
24
+ self.session = session
25
25
  self.model = model
26
-
27
- #######################
28
- # 2. 쿼리 빌딩 #
29
- #######################
30
- def _build_base_query(self) -> Select:
31
- """기본 쿼리를 생성합니다.
32
-
33
- Returns:
34
- Select: 기본 쿼리
35
- """
36
- return select(self.model)
37
-
38
- #######################
39
- # 3. 전차리 영역 #
40
- #######################
41
- def _apply_exact_match(self, stmt: Select, field_name: str, value: Any) -> Select:
42
- """정확한 값 매칭 조건을 적용합니다.
43
-
44
- Args:
45
- stmt (Select): 쿼리문
46
- field_name (str): 필드명
47
- value (Any): 매칭할 값
48
-
49
- Returns:
50
- Select: 조건이 적용된 쿼리
51
- """
52
- return stmt.where(getattr(self.model, field_name) == value)
53
-
54
- def _apply_like_match(self, stmt: Select, field_name: str, value: str) -> Select:
55
- """LIKE 검색 조건을 적용합니다.
56
-
57
- Args:
58
- stmt (Select): 쿼리문
59
- field_name (str): 필드명
60
- value (str): 검색할 값
61
-
62
- Returns:
63
- Select: 조건이 적용된 쿼리
64
- """
65
- return stmt.where(getattr(self.model, field_name).ilike(f"%{value}%"))
66
-
67
- def _apply_relation_match(self, stmt: Select, relations: List[str], field_name: str, operator: str, value: Any) -> Select:
68
- """관계 테이블 검색 조건을 적용합니다."""
69
- current = self.model
70
-
71
- # 관계 체인 따라가기
72
- for i in range(len(relations)-1):
73
- current = getattr(current, relations[i]).property.mapper.class_
74
-
75
- # 마지막 모델과 필드
76
- final_model = getattr(current, relations[-1]).property.mapper.class_
77
-
78
- # 중첩된 EXISTS 절 생성
79
- current = self.model
80
- subq = select(1)
81
-
82
- # 첫 번째 관계
83
- next_model = getattr(current, relations[0]).property.mapper.class_
84
- subq = subq.where(getattr(next_model, 'ulid') == getattr(current, f"{relations[0]}_ulid"))
85
-
86
- # 중간 관계들
87
- for i in range(1, len(relations)):
88
- prev_model = next_model
89
- next_model = getattr(prev_model, relations[i]).property.mapper.class_
90
- subq = subq.where(getattr(next_model, 'ulid') == getattr(prev_model, f"{relations[i]}_ulid"))
91
-
92
- # 최종 검색 조건
93
- subq = subq.where(getattr(final_model, field_name).__getattribute__(operator)(value))
94
-
95
- return stmt.where(subq.exists())
96
-
97
- def _apply_ordering(self, stmt: Select, order_by: List[str]) -> Select:
98
- """정렬 조건을 적용합니다.
99
-
100
- Args:
101
- stmt (Select): 쿼리문
102
- order_by (List[str]): 정렬 기준 필드 목록 (예: ["name", "-created_at"])
103
-
104
- Returns:
105
- Select: 정렬이 적용된 쿼리
106
- """
107
- for field in order_by:
108
- if field.startswith("-"):
109
- field_name = field[1:]
110
- stmt = stmt.order_by(getattr(self.model, field_name).desc())
111
- else:
112
- stmt = stmt.order_by(getattr(self.model, field).asc())
113
- return stmt
114
-
115
- def _apply_pagination(self, stmt: Select, skip: int = 0, limit: int = 100) -> Select:
116
- """페이징을 적용합니다.
117
-
118
- Args:
119
- stmt (Select): 쿼리문
120
- skip (int): 건너뛸 레코드 수
121
- limit (int): 조회할 최대 레코드 수
122
-
123
- Returns:
124
- Select: 페이징이 적용된 쿼리
125
- """
126
- return stmt.offset(skip).limit(limit)
127
-
128
- def _apply_joins(self, stmt: Select, joins: List[str]) -> Select:
129
- """조인을 적용합니다.
130
-
131
- Args:
132
- stmt (Select): 쿼리문
133
- joins (List[str]): 조인할 관계명 목록
134
-
135
- Returns:
136
- Select: 조인이 적용된 쿼리
137
- """
138
- for join in joins:
139
- stmt = stmt.options(joinedload(getattr(self.model, join)))
140
- return stmt
141
-
142
- def _build_jsonb_condition(self, model: Any, field_path: str, value: str) -> Any:
143
- """JSONB 필드에 대한 검색 조건을 생성합니다.
144
-
145
- Args:
146
- model: 대상 모델
147
- field_path (str): JSONB 키 경로 (예: "address", "name.first")
148
- value (str): 검색할 값
149
-
150
- Returns:
151
- Any: SQLAlchemy 검색 조건
152
- """
153
- # JSONB 경로가 중첩된 경우 (예: "name.first")
154
- if "." in field_path:
155
- path_parts = field_path.split(".")
156
- jsonb_path = "{" + ",".join(path_parts) + "}"
157
- return model.extra_data[jsonb_path].astext.ilike(f"%{value}%")
158
- # 단일 키인 경우
159
- return model.extra_data[field_path].astext.ilike(f"%{value}%")
160
-
161
- def _apply_jsonb_match(self, stmt: Select, relations: List[str], json_key: str, value: str) -> Select:
162
- """JSONB 필드 검색 조건을 적용합니다.
163
-
164
- Args:
165
- stmt (Select): 쿼리문
166
- relations (List[str]): 관계 테이블 경로
167
- json_key (str): JSONB 키 경로
168
- value (str): 검색할 값
169
-
170
- Returns:
171
- Select: 조건이 적용된 쿼리
172
- """
173
- current = self.model
174
-
175
- # 단일 모델 검색
176
- if not relations:
177
- condition = self._build_jsonb_condition(current, json_key, value)
178
- return stmt.where(condition)
179
-
180
- # 관계 모델 검색
181
- for i in range(len(relations)-1):
182
- current = getattr(current, relations[i]).property.mapper.class_
183
-
184
- final_model = getattr(current, relations[-1]).property.mapper.class_
185
-
186
- # 관계 체인 구성
187
- if len(relations) == 1:
188
- condition = getattr(self.model, relations[0]).has(
189
- self._build_jsonb_condition(final_model, json_key, value)
190
- )
191
- else:
192
- condition = getattr(self.model, relations[0]).has(
193
- getattr(final_model, relations[-1]).has(
194
- self._build_jsonb_condition(final_model, json_key, value)
195
- )
26
+
27
+ @property
28
+ def session(self):
29
+ """현재 세션을 반환합니다."""
30
+ if self._session is None:
31
+ raise CustomException(
32
+ ErrorCode.DB_CONNECTION_ERROR,
33
+ detail="Database session is not set",
34
+ source_function=f"{self.__class__.__name__}.session"
196
35
  )
197
-
198
- return stmt.where(condition)
199
-
200
- def _apply_search_params(self, stmt, search_params: Dict[str, Any]):
201
- """검색 파라미터를 적용합니다."""
202
- if not search_params:
203
- return stmt
204
-
205
- for key, value in search_params.items():
206
- if not value.get("value"):
207
- continue
208
-
209
- conditions = []
210
- for field in value.get("fields", []):
211
- parts = field.split('.')
212
-
213
- if len(parts) == 1:
214
- # 직접 필드 검색
215
- condition = self._apply_like_match(stmt, parts[0], value["value"]).whereclause
216
- elif 'extra_data' in parts:
217
- # JSONB 필드 검색
218
- extra_data_idx = parts.index('extra_data')
219
- tables = parts[:extra_data_idx]
220
- json_key = ".".join(parts[extra_data_idx + 1:])
221
- condition = self._apply_jsonb_match(
222
- stmt,
223
- tables,
224
- json_key,
225
- value["value"]
226
- ).whereclause
227
- else:
228
- # 관계 테이블 검색
229
- condition = self._apply_relation_match(
230
- stmt,
231
- parts[:-1],
232
- parts[-1],
233
- "ilike",
234
- f"%{value['value']}%"
235
- ).whereclause
236
-
237
- conditions.append(condition)
238
-
239
- if conditions:
240
- stmt = stmt.where(or_(*conditions))
241
-
242
- return stmt
243
-
244
- def _apply_filters(self, stmt, filters: Dict[str, Any]):
245
- """일반 필터를 적용합니다."""
246
- for key, value in filters.items():
247
- if value is None:
248
- continue
249
-
250
- if "." in key:
251
- # 관계 테이블 필터
252
- relation, field = key.split(".")
253
- stmt = self._apply_relation_match(stmt, relation, field, "__eq__", value)
254
- else:
255
- # 일반 필드 필터
256
- stmt = stmt.where(getattr(self.model, key) == value)
257
-
258
- return stmt
36
+ return self._session
37
+
38
+ @session.setter
39
+ def session(self, value):
40
+ """세션을 설정합니다."""
41
+ self._session = value
259
42
 
260
43
  #######################
261
- # 4. CRUD 작업 #
44
+ # 2. CRUD 작업 #
262
45
  #######################
263
46
  async def get(
264
47
  self,
265
48
  ulid: str
266
49
  ) -> Optional[Dict[str, Any]]:
267
- """ULID로 엔티티를 조회합니다.
268
-
269
- Args:
270
- ulid (str): 조회할 엔티티의 ULID
271
-
272
- Returns:
273
- Optional[Dict[str, Any]]: 조회된 엔티티, 없으면 None
274
-
275
- Raises:
276
- CustomException: 데이터베이스 작업 중 오류 발생 시
277
- """
50
+ """ULID로 엔티티를 조회합니다."""
278
51
  try:
279
52
  stmt = select(self.model).filter_by(ulid=ulid, is_deleted=False)
280
- result = await self.db_service.execute(stmt)
53
+ result = await self.session.execute(stmt)
281
54
  entity = result.scalars().unique().first()
282
55
 
283
56
  if not entity:
@@ -298,14 +71,7 @@ class BaseRepository(Generic[ModelType]):
298
71
  source_function=f"{self.__class__.__name__}.get",
299
72
  original_error=e
300
73
  )
301
- except Exception as e:
302
- raise CustomException(
303
- ErrorCode.DB_QUERY_ERROR,
304
- detail=f"Unexpected repository error in {self.model.__tablename__}: {str(e)}",
305
- source_function=f"{self.__class__.__name__}.get",
306
- original_error=e
307
- )
308
-
74
+
309
75
  async def list(
310
76
  self,
311
77
  skip: int = 0,
@@ -328,7 +94,7 @@ class BaseRepository(Generic[ModelType]):
328
94
  # 페이지네이션 적용
329
95
  stmt = stmt.limit(limit).offset(skip)
330
96
 
331
- result = await self.db_service.db.execute(stmt)
97
+ result = await self.session.execute(stmt)
332
98
  return result.scalars().unique().all()
333
99
 
334
100
  except SQLAlchemyError as e:
@@ -340,165 +106,103 @@ class BaseRepository(Generic[ModelType]):
340
106
  )
341
107
 
342
108
  async def create(self, data: Dict[str, Any]) -> ModelType:
343
- """새로운 엔티티를 생성합니다.
344
-
345
- Args:
346
- data (Dict[str, Any]): 생성할 엔티티 데이터
347
-
348
- Returns:
349
- ModelType: 생성된 엔티티
350
-
351
- Raises:
352
- CustomException: 데이터베이스 작업 중 오류 발생 시
353
- """
109
+ """새로운 엔티티를 생성합니다."""
354
110
  try:
355
- return await self.db_service.create_entity(self.model, data)
356
- except CustomException as e:
357
- e.detail = f"Repository create error for {self.model.__tablename__}: {e.detail}"
358
- e.source_function = f"{self.__class__.__name__}.create -> {e.source_function}"
359
- raise e
111
+ entity = self.model(**data)
112
+ self.session.add(entity)
113
+ await self.session.flush()
114
+ await self.session.refresh(entity)
115
+ return entity
360
116
  except IntegrityError as e:
117
+ await self.session.rollback()
361
118
  self._handle_integrity_error(e, "create", data)
362
119
  except SQLAlchemyError as e:
120
+ await self.session.rollback()
363
121
  raise CustomException(
364
122
  ErrorCode.DB_CREATE_ERROR,
365
123
  detail=f"Database create error in {self.model.__tablename__}: {str(e)}",
366
124
  source_function=f"{self.__class__.__name__}.create",
367
125
  original_error=e
368
126
  )
369
- except Exception as e:
370
- raise CustomException(
371
- ErrorCode.DB_CREATE_ERROR,
372
- detail=f"Unexpected repository create error in {self.model.__tablename__}: {str(e)}",
373
- source_function=f"{self.__class__.__name__}.create",
374
- original_error=e
375
- )
376
127
 
377
128
  async def update(self, ulid: str, data: Dict[str, Any]) -> Optional[ModelType]:
378
- """기존 엔티티를 수정합니다.
379
-
380
- Args:
381
- ulid (str): 수정할 엔티티의 ULID
382
- data (Dict[str, Any]): 수정할 데이터
383
-
384
- Returns:
385
- Optional[ModelType]: 수정된 엔티티, 없으면 None
386
-
387
- Raises:
388
- CustomException: 데이터베이스 작업 중 오류 발생 시
389
- """
129
+ """기존 엔티티를 수정합니다."""
390
130
  try:
391
- entity = await self.db_service.update_entity(
392
- self.model,
393
- {"ulid": ulid, "is_deleted": False},
394
- data
395
- )
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
+
396
135
  if not entity:
397
136
  raise CustomException(
398
137
  ErrorCode.DB_NO_RESULT,
399
138
  detail=f"{self.model.__tablename__}|ulid|{ulid}",
400
139
  source_function=f"{self.__class__.__name__}.update"
401
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)
402
147
  return entity
403
- except CustomException as e:
404
- e.detail = f"Repository update error for {self.model.__tablename__}: {e.detail}"
405
- e.source_function = f"{self.__class__.__name__}.update -> {e.source_function}"
406
- raise e
148
+
407
149
  except IntegrityError as e:
150
+ await self.session.rollback()
408
151
  self._handle_integrity_error(e, "update", data)
409
152
  except SQLAlchemyError as e:
153
+ await self.session.rollback()
410
154
  raise CustomException(
411
155
  ErrorCode.DB_UPDATE_ERROR,
412
156
  detail=f"Database update error in {self.model.__tablename__}: {str(e)}",
413
157
  source_function=f"{self.__class__.__name__}.update",
414
158
  original_error=e
415
159
  )
416
- except Exception as e:
417
- raise CustomException(
418
- ErrorCode.DB_UPDATE_ERROR,
419
- detail=f"Unexpected repository update error in {self.model.__tablename__}: {str(e)}",
420
- source_function=f"{self.__class__.__name__}.update",
421
- original_error=e
422
- )
423
160
 
424
161
  async def delete(self, ulid: str) -> bool:
425
- """엔티티를 소프트 삭제합니다 (is_deleted = True).
426
-
427
- Args:
428
- ulid (str): 삭제할 엔티티의 ULID
429
-
430
- Returns:
431
- bool: 삭제 성공 여부
432
-
433
- Raises:
434
- CustomException: 데이터베이스 작업 중 오류 발생 시
435
- """
162
+ """엔티티를 소프트 삭제합니다."""
436
163
  try:
437
- entity = await self.db_service.soft_delete_entity(self.model, ulid)
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
+
438
168
  if not entity:
439
169
  raise CustomException(
440
170
  ErrorCode.DB_NO_RESULT,
441
171
  detail=f"{self.model.__tablename__}|ulid|{ulid}",
442
172
  source_function=f"{self.__class__.__name__}.delete"
443
173
  )
174
+
175
+ entity.is_deleted = True
176
+ await self.session.flush()
444
177
  return True
445
- except CustomException as e:
446
- e.detail = f"Repository delete error for {self.model.__tablename__}: {e.detail}"
447
- e.source_function = f"{self.__class__.__name__}.delete -> {e.source_function}"
448
- raise e
449
- except IntegrityError as e:
450
- self._handle_integrity_error(e, "delete")
178
+
451
179
  except SQLAlchemyError as e:
180
+ await self.session.rollback()
452
181
  raise CustomException(
453
182
  ErrorCode.DB_DELETE_ERROR,
454
183
  detail=f"Database delete error in {self.model.__tablename__}: {str(e)}",
455
184
  source_function=f"{self.__class__.__name__}.delete",
456
185
  original_error=e
457
186
  )
458
- except Exception as e:
459
- raise CustomException(
460
- ErrorCode.DB_DELETE_ERROR,
461
- detail=f"Unexpected repository delete error in {self.model.__tablename__}: {str(e)}",
462
- source_function=f"{self.__class__.__name__}.delete",
463
- original_error=e
464
- )
465
-
466
- async def real_row_delete(self, ulid: str) -> bool:
467
- """엔티티를 실제로 삭제합니다.
468
-
469
- Args:
470
- ulid (str): 삭제할 엔티티의 ULID
471
187
 
472
- Returns:
473
- bool: 삭제 성공 여부
474
-
475
- Raises:
476
- CustomException: 데이터베이스 작업 중 오류 발생 시
477
- """
188
+ async def real_delete(self, ulid: str) -> bool:
189
+ """엔티티를 실제로 삭제합니다."""
478
190
  try:
479
- entity = await self.db_service.retrieve_entity(
480
- self.model,
481
- {"ulid": ulid}
482
- )
191
+ stmt = select(self.model).filter_by(ulid=ulid)
192
+ result = await self.session.execute(stmt)
193
+ entity = result.scalars().first()
194
+
483
195
  if entity:
484
- await self.db_service.delete_entity(entity)
196
+ await self.session.delete(entity)
197
+ await self.session.flush()
485
198
  return True
486
199
  return False
487
- except CustomException as e:
488
- e.detail = f"Repository real delete error for {self.model.__tablename__}: {e.detail}"
489
- e.source_function = f"{self.__class__.__name__}.real_row_delete -> {e.source_function}"
490
- raise e
200
+
491
201
  except SQLAlchemyError as e:
202
+ await self.session.rollback()
492
203
  raise CustomException(
493
204
  ErrorCode.DB_DELETE_ERROR,
494
205
  detail=f"Database real delete error in {self.model.__tablename__}: {str(e)}",
495
- source_function=f"{self.__class__.__name__}.real_row_delete",
496
- original_error=e
497
- )
498
- except Exception as e:
499
- raise CustomException(
500
- ErrorCode.DB_DELETE_ERROR,
501
- detail=f"Unexpected repository real delete error in {self.model.__tablename__}: {str(e)}",
502
- source_function=f"{self.__class__.__name__}.real_row_delete",
206
+ source_function=f"{self.__class__.__name__}.real_delete",
503
207
  original_error=e
504
208
  )
@@ -9,6 +9,7 @@ from .base_repository import BaseRepository
9
9
  from .security import hash_password
10
10
  from fastapi import Request
11
11
  from ulid import ULID
12
+ from sqlalchemy import select
12
13
 
13
14
  ModelType = TypeVar("ModelType", bound=DeclarativeBase)
14
15
 
@@ -30,11 +31,29 @@ class BaseService(Generic[ModelType]):
30
31
  self.repository = repository
31
32
  self.model = repository.model
32
33
  self.additional_models = additional_models or {}
33
- self.db_service = repository.db_service
34
+ self._session = None
34
35
  self.searchable_fields = {
35
36
  "name": {"type": "text", "description": "이름"},
36
37
  "organization_ulid": {"type": "exact", "description": "조직 ID"}
37
38
  }
39
+
40
+ @property
41
+ def session(self):
42
+ """현재 세션을 반환합니다."""
43
+ if self._session is None:
44
+ raise CustomException(
45
+ ErrorCode.DB_CONNECTION_ERROR,
46
+ detail="Database session is not set",
47
+ source_function=f"{self.__class__.__name__}.session"
48
+ )
49
+ return self._session
50
+
51
+ @session.setter
52
+ def session(self, value):
53
+ """세션을 설정합니다."""
54
+ self._session = value
55
+ if hasattr(self.repository, 'session'):
56
+ self.repository.session = value
38
57
 
39
58
  #########################
40
59
  # 2. 이벤트 처리 메서드 #
@@ -448,18 +467,7 @@ class BaseService(Generic[ModelType]):
448
467
  )
449
468
 
450
469
  async def delete(self, ulid: str, model_name: str = None) -> bool:
451
- """엔티티를 소프트 삭제합니다 (is_deleted = True).
452
-
453
- Args:
454
- ulid (str): 삭제할 엔티티의 ULID
455
- model_name (str, optional): 삭제할 모델 이름. Defaults to None.
456
-
457
- Returns:
458
- bool: 삭제 성공 여부
459
-
460
- Raises:
461
- CustomException: 데이터베이스 작업 중 오류 발생 시
462
- """
470
+ """엔티티를 소프트 삭제합니다 (is_deleted = True)."""
463
471
  try:
464
472
  if model_name:
465
473
  if model_name not in self.additional_models:
@@ -468,23 +476,24 @@ class BaseService(Generic[ModelType]):
468
476
  detail=f"Model {model_name} not registered",
469
477
  source_function=f"{self.__class__.__name__}.delete"
470
478
  )
471
- entity = await self.db_service.soft_delete_entity(self.additional_models[model_name], ulid)
479
+
480
+ stmt = select(self.additional_models[model_name]).filter_by(ulid=ulid, is_deleted=False)
481
+ result = await self.session.execute(stmt)
482
+ entity = result.scalars().first()
483
+
472
484
  if not entity:
473
485
  raise CustomException(
474
486
  ErrorCode.NOT_FOUND,
475
487
  detail=f"{self.additional_models[model_name].__tablename__}|ulid|{ulid}",
476
488
  source_function=f"{self.__class__.__name__}.delete"
477
489
  )
490
+
491
+ entity.is_deleted = True
492
+ await self.session.flush()
478
493
  return True
479
494
 
480
- entity = await self.repository.delete(ulid)
481
- if not entity:
482
- raise CustomException(
483
- ErrorCode.NOT_FOUND,
484
- detail=f"{self.model.__tablename__}|ulid|{ulid}",
485
- source_function=f"{self.__class__.__name__}.delete"
486
- )
487
- return True
495
+ return await self.repository.delete(ulid)
496
+
488
497
  except CustomException as e:
489
498
  raise e
490
499
  except Exception as e:
@@ -549,20 +558,7 @@ class BaseService(Generic[ModelType]):
549
558
  request: Request | None = None,
550
559
  response_model: Any = None
551
560
  ) -> List[Dict[str, Any]]:
552
- """엔티티 목록을 조회합니다.
553
-
554
- Args:
555
- skip (int, optional): 건너뛸 레코드 수. Defaults to 0.
556
- limit (int, optional): 조회할 최대 레코드 수. Defaults to 100.
557
- filters (Dict[str, Any] | None, optional): 필터링 조건. Defaults to None.
558
- search_params (Dict[str, Any] | None, optional): 검색 파라미터. Defaults to None.
559
- model_name (str | None, optional): 조회할 모델 이름. Defaults to None.
560
- request (Request | None, optional): 요청 객체. Defaults to None.
561
- response_model (Any, optional): 응답 스키마. Defaults to None.
562
-
563
- Returns:
564
- List[Dict[str, Any]]: 엔티티 목록
565
- """
561
+ """엔티티 목록을 조회합니다."""
566
562
  try:
567
563
  if model_name:
568
564
  if model_name not in self.additional_models:
@@ -571,21 +567,28 @@ class BaseService(Generic[ModelType]):
571
567
  detail=f"Model {model_name} not registered",
572
568
  source_function=f"{self.__class__.__name__}.list"
573
569
  )
574
- entities = await self.db_service.list_entities(
575
- self.additional_models[model_name],
576
- skip=skip,
577
- limit=limit,
578
- filters=filters
570
+
571
+ stmt = select(self.additional_models[model_name]).where(
572
+ self.additional_models[model_name].is_deleted == False
579
573
  )
574
+
575
+ if filters:
576
+ for key, value in filters.items():
577
+ if value is not None:
578
+ stmt = stmt.where(getattr(self.additional_models[model_name], key) == value)
579
+
580
+ stmt = stmt.offset(skip).limit(limit)
581
+ result = await self.session.execute(stmt)
582
+ entities = result.scalars().all()
583
+
580
584
  return [self._process_response(entity, response_model) for entity in entities]
581
585
 
582
- entities = await self.repository.list(
586
+ return await self.repository.list(
583
587
  skip=skip,
584
588
  limit=limit,
585
589
  filters=filters,
586
590
  search_params=search_params
587
591
  )
588
- return [self._process_response(entity, response_model) for entity in entities]
589
592
 
590
593
  except CustomException as e:
591
594
  e.detail = f"Service list error for {self.repository.model.__tablename__}: {e.detail}"