aiteamutils 0.2.51__py3-none-any.whl → 0.2.53__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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}"