aiteamutils 0.2.161__py3-none-any.whl → 0.2.163__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/base_model.py +5 -5
- aiteamutils/database.py +87 -55
- aiteamutils/exceptions.py +6 -0
- aiteamutils/version.py +1 -1
- {aiteamutils-0.2.161.dist-info → aiteamutils-0.2.163.dist-info}/METADATA +1 -1
- {aiteamutils-0.2.161.dist-info → aiteamutils-0.2.163.dist-info}/RECORD +7 -8
- aiteamutils/models.py +0 -1
- {aiteamutils-0.2.161.dist-info → aiteamutils-0.2.163.dist-info}/WHEEL +0 -0
aiteamutils/base_model.py
CHANGED
@@ -126,25 +126,25 @@ class BaseFileModel(BaseColumn):
|
|
126
126
|
)
|
127
127
|
mime_type: Mapped[str] = mapped_column(
|
128
128
|
String,
|
129
|
-
nullable=
|
129
|
+
nullable=True,
|
130
130
|
doc="MIME 타입"
|
131
131
|
)
|
132
132
|
mime_type_main: Mapped[str] = mapped_column(
|
133
133
|
String,
|
134
|
-
nullable=
|
134
|
+
nullable=True,
|
135
135
|
doc="MIME 타입 주 분류 (예: image, video, application 등)"
|
136
136
|
)
|
137
137
|
mime_type_sub: Mapped[str] = mapped_column(
|
138
138
|
String,
|
139
|
-
nullable=
|
139
|
+
nullable=True,
|
140
140
|
doc="MIME 타입 부 분류 (예: jpeg, mp4, pdf 등)"
|
141
141
|
)
|
142
142
|
size: Mapped[int] = mapped_column(
|
143
|
-
nullable=
|
143
|
+
nullable=True,
|
144
144
|
doc="파일 크기(bytes)"
|
145
145
|
)
|
146
146
|
checksum: Mapped[str] = mapped_column(
|
147
147
|
String,
|
148
|
-
nullable=
|
148
|
+
nullable=True,
|
149
149
|
doc="파일 체크섬"
|
150
150
|
)
|
aiteamutils/database.py
CHANGED
@@ -8,7 +8,8 @@ from typing import (
|
|
8
8
|
List,
|
9
9
|
Optional,
|
10
10
|
AsyncGenerator,
|
11
|
-
Union
|
11
|
+
Union,
|
12
|
+
Tuple
|
12
13
|
)
|
13
14
|
from sqlalchemy.ext.asyncio import AsyncSession
|
14
15
|
from sqlalchemy import select, and_, or_
|
@@ -20,12 +21,19 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
20
21
|
from fastapi import Request
|
21
22
|
from ulid import ULID
|
22
23
|
from sqlalchemy import MetaData, Table, insert
|
24
|
+
from sqlalchemy.sql.elements import BinaryExpression
|
23
25
|
|
24
26
|
#패키지 라이브러리
|
25
27
|
from .exceptions import ErrorCode, CustomException
|
26
28
|
|
27
29
|
ModelType = TypeVar("ModelType", bound=DeclarativeBase)
|
28
30
|
|
31
|
+
JoinTarget = Union[
|
32
|
+
DeclarativeBase, # 모델
|
33
|
+
Tuple[DeclarativeBase, BinaryExpression], # (모델, 조인 조건)
|
34
|
+
Tuple[DeclarativeBase, BinaryExpression, str] # (모델, 조인 조건, 조인 타입)
|
35
|
+
]
|
36
|
+
|
29
37
|
##################
|
30
38
|
# 전처리 #
|
31
39
|
##################
|
@@ -255,6 +263,61 @@ def build_conditions(
|
|
255
263
|
##################
|
256
264
|
# 쿼리 실행 #
|
257
265
|
##################
|
266
|
+
async def execute_query(
|
267
|
+
session: AsyncSession,
|
268
|
+
model: Type[ModelType],
|
269
|
+
explicit_joins: Optional[List[JoinTarget]] = None,
|
270
|
+
loading_joins: Optional[List[Any]] = None,
|
271
|
+
conditions: Optional[List[BinaryExpression]] = None,
|
272
|
+
) -> Select:
|
273
|
+
"""
|
274
|
+
쿼리 실행을 위한 공통 함수
|
275
|
+
|
276
|
+
Args:
|
277
|
+
session: 데이터베이스 세션
|
278
|
+
model: 기본 모델
|
279
|
+
explicit_joins: 명시적 조인 설정
|
280
|
+
- 모델만 전달: 기본 outer join
|
281
|
+
- (모델, 조건): 조건부 outer join
|
282
|
+
- (모델, 조건, 'inner'/'left'/'right'): 조인 타입 지정
|
283
|
+
loading_joins: Relationship 로딩 설정 (joinedload, selectinload 등)
|
284
|
+
conditions: WHERE 조건들
|
285
|
+
"""
|
286
|
+
# 1. 기본 쿼리 생성
|
287
|
+
query = select(model)
|
288
|
+
|
289
|
+
# 2. 명시적 조인 처리
|
290
|
+
if explicit_joins:
|
291
|
+
for join_target in explicit_joins:
|
292
|
+
if isinstance(join_target, DeclarativeBase):
|
293
|
+
# 모델만 전달된 경우
|
294
|
+
query = query.outerjoin(join_target)
|
295
|
+
elif isinstance(join_target, tuple):
|
296
|
+
if len(join_target) == 2:
|
297
|
+
# (모델, 조건)이 전달된 경우
|
298
|
+
target_model, join_condition = join_target
|
299
|
+
query = query.outerjoin(target_model, join_condition)
|
300
|
+
elif len(join_target) == 3:
|
301
|
+
# (모델, 조건, 조인타입)이 전달된 경우
|
302
|
+
target_model, join_condition, join_type = join_target
|
303
|
+
if join_type == 'inner':
|
304
|
+
query = query.join(target_model, join_condition)
|
305
|
+
elif join_type == 'left':
|
306
|
+
query = query.outerjoin(target_model, join_condition)
|
307
|
+
elif join_type == 'right':
|
308
|
+
query = query.outerjoin(target_model, join_condition, full=True)
|
309
|
+
|
310
|
+
# 3. Relationship 로딩 설정
|
311
|
+
if loading_joins:
|
312
|
+
for join_option in loading_joins:
|
313
|
+
query = query.options(join_option)
|
314
|
+
|
315
|
+
# 4. 조건 적용
|
316
|
+
if conditions:
|
317
|
+
query = query.where(and_(*conditions))
|
318
|
+
|
319
|
+
return query
|
320
|
+
|
258
321
|
async def create_entity(
|
259
322
|
session: AsyncSession,
|
260
323
|
model: Type[ModelType],
|
@@ -428,49 +491,22 @@ async def list_entities(
|
|
428
491
|
skip: int = 0,
|
429
492
|
limit: int = 100,
|
430
493
|
filters: Optional[List[Dict[str, Any]]] = None,
|
431
|
-
explicit_joins: Optional[List[
|
494
|
+
explicit_joins: Optional[List[JoinTarget]] = None,
|
432
495
|
loading_joins: Optional[List[Any]] = None,
|
433
496
|
order: Optional[List[Dict[str, str]]] = None
|
434
497
|
) -> List[Dict[str, Any]]:
|
435
|
-
"""
|
436
|
-
엔터티 리스트를 필터 및 조건에 따라 가져오는 함수.
|
437
|
-
|
438
|
-
Args:
|
439
|
-
session: SQLAlchemy AsyncSession.
|
440
|
-
model: SQLAlchemy 모델.
|
441
|
-
skip: 페이지네이션 시작 위치.
|
442
|
-
limit: 페이지네이션 크기.
|
443
|
-
filters: 필터 조건 딕셔너리.
|
444
|
-
예시:
|
445
|
-
filters = {
|
446
|
-
"search": {"field": "username", "operator": "like", "value": "%admin%"},
|
447
|
-
"name": {"field": "name", "operator": "like", "value": "%John%"},
|
448
|
-
"role_ulid": {"field": "role_ulid", "operator": "eq", "value": "1234"}
|
449
|
-
}
|
450
|
-
|
451
|
-
joins: 조인 옵션.
|
452
|
-
예시:
|
453
|
-
joins = [
|
454
|
-
selectinload(YourModel.related_field), # 관련된 필드를 함께 로드
|
455
|
-
joinedload(YourModel.another_related_field) # 다른 관계된 필드를 조인
|
456
|
-
]
|
457
|
-
|
458
|
-
Returns:
|
459
|
-
List[Dict[str, Any]]: 쿼리 결과 리스트.
|
460
|
-
"""
|
461
498
|
try:
|
499
|
+
# 필터 조건 생성
|
500
|
+
conditions = build_conditions(filters, model) if filters else None
|
501
|
+
|
462
502
|
# 기본 쿼리 생성
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
# 필터 조건 적용
|
471
|
-
if filters:
|
472
|
-
conditions = build_conditions(filters, model)
|
473
|
-
query = query.where(and_(*conditions))
|
503
|
+
query = await execute_query(
|
504
|
+
session=session,
|
505
|
+
model=model,
|
506
|
+
explicit_joins=explicit_joins,
|
507
|
+
loading_joins=loading_joins,
|
508
|
+
conditions=conditions
|
509
|
+
)
|
474
510
|
|
475
511
|
# 정렬 조건 적용
|
476
512
|
if order:
|
@@ -505,25 +541,21 @@ async def get_entity(
|
|
505
541
|
session: AsyncSession,
|
506
542
|
model: Type[ModelType],
|
507
543
|
conditions: Dict[str, Any],
|
508
|
-
explicit_joins: Optional[List[
|
544
|
+
explicit_joins: Optional[List[JoinTarget]] = None,
|
509
545
|
loading_joins: Optional[List[Any]] = None
|
510
546
|
) -> ModelType:
|
511
547
|
try:
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
if conditions:
|
525
|
-
for key, value in conditions.items():
|
526
|
-
query = query.where(getattr(model, key) == value)
|
548
|
+
# 조건을 리스트로 변환
|
549
|
+
condition_list = [getattr(model, key) == value for key, value in conditions.items()]
|
550
|
+
|
551
|
+
# 쿼리 실행
|
552
|
+
query = await execute_query(
|
553
|
+
session=session,
|
554
|
+
model=model,
|
555
|
+
explicit_joins=explicit_joins,
|
556
|
+
loading_joins=loading_joins,
|
557
|
+
conditions=condition_list
|
558
|
+
)
|
527
559
|
|
528
560
|
result = await session.execute(query)
|
529
561
|
return result.scalars().unique().one_or_none()
|
aiteamutils/exceptions.py
CHANGED
@@ -77,6 +77,12 @@ class ErrorCode(Enum):
|
|
77
77
|
TOKEN_ERROR = ErrorResponse(5005, "TOKEN_ERROR", 401, "토큰 오류")
|
78
78
|
DELETE_ERROR = ErrorResponse(5006, "DELETE_ERROR", 400, "삭제 오류")
|
79
79
|
|
80
|
+
# File 관련 에러: 6000번대
|
81
|
+
FILE_NOT_FOUND = ErrorResponse(6001, "FILE_NOT_FOUND", 404, "파일을 찾을 수 없습니다")
|
82
|
+
FILE_UPLOAD_ERROR = ErrorResponse(6002, "FILE_UPLOAD_ERROR", 500, "파일 업로드 오류")
|
83
|
+
FILE_DOWNLOAD_ERROR = ErrorResponse(6003, "FILE_DOWNLOAD_ERROR", 500, "파일 다운로드 오류")
|
84
|
+
FILE_DELETE_ERROR = ErrorResponse(6004, "FILE_DELETE_ERROR", 500, "파일 삭제 오류")
|
85
|
+
FILE_PROCESSING_ERROR = ErrorResponse(6005, "FILE_PROCESSING_ERROR", 500, "파일 처리 오류")
|
80
86
|
|
81
87
|
class CustomException(Exception):
|
82
88
|
"""사용자 정의 예외 클래스"""
|
aiteamutils/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
"""버전 정보"""
|
2
|
-
__version__ = "0.2.
|
2
|
+
__version__ = "0.2.163"
|
@@ -1,17 +1,16 @@
|
|
1
1
|
aiteamutils/__init__.py,sha256=kRBpRjark0M8ZwFfmKiMFol6CbIILN3WE4f6_P6iIq0,1089
|
2
|
-
aiteamutils/base_model.py,sha256=
|
2
|
+
aiteamutils/base_model.py,sha256=Jw5Fyfrtdc_jyg-LmC35c-D6w3-2_dh2CBeiKjM9GPE,4497
|
3
3
|
aiteamutils/base_repository.py,sha256=Oy2zE1i5qx60Xf1tnsaKLyFWapiPqt5JH8NejwNrPWg,4647
|
4
4
|
aiteamutils/base_service.py,sha256=JIeRtFn1Ll4Qcq3v88pgS9lmEQLQoVyyOKqYR8n02S4,21192
|
5
5
|
aiteamutils/cache.py,sha256=07xBGlgAwOTAdY5mnMOQJ5EBxVwe8glVD7DkGEkxCtw,1373
|
6
6
|
aiteamutils/config.py,sha256=YdalpJb70-txhGJAS4aaKglEZAFVWgfzw5BXSWpkUz4,3232
|
7
|
-
aiteamutils/database.py,sha256=
|
7
|
+
aiteamutils/database.py,sha256=zXYhdUVgAisz4BuXZPPbvVLZ9lwRe7aVQP8J1AVE88Y,21771
|
8
8
|
aiteamutils/enums.py,sha256=7WLqlcJqQWtETAga2WAxNp3dJTQIAd2TW-4WzkoHHa8,2498
|
9
|
-
aiteamutils/exceptions.py,sha256=
|
9
|
+
aiteamutils/exceptions.py,sha256=sgIVulllKMM9InTltaB7VD6i7DiQvCoycexsV-BiIBY,16570
|
10
10
|
aiteamutils/files.py,sha256=fxnCu9rErd4vCovMi0jy4adLUiA7rx_q4RdOL4wSgsU,14258
|
11
|
-
aiteamutils/models.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
|
12
11
|
aiteamutils/security.py,sha256=McUl3t5Z5SyUDVUHymHdDkYyF4YSeg4g9fFMML4W6Kw,11630
|
13
12
|
aiteamutils/validators.py,sha256=_WHN6jqJQzKM5uPTg-Da8U2qqevS84XeKMkCCF4C_lY,9591
|
14
|
-
aiteamutils/version.py,sha256=
|
15
|
-
aiteamutils-0.2.
|
16
|
-
aiteamutils-0.2.
|
17
|
-
aiteamutils-0.2.
|
13
|
+
aiteamutils/version.py,sha256=MbP3Sxzx6eYugMlpPFw2qfjDxYmMdJwa_4K4MStAl1s,43
|
14
|
+
aiteamutils-0.2.163.dist-info/METADATA,sha256=pgSEMmDcvxJrKq3Y-CmLEuXgd2K5vKvm8SQfa3jIuvA,1743
|
15
|
+
aiteamutils-0.2.163.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
16
|
+
aiteamutils-0.2.163.dist-info/RECORD,,
|
aiteamutils/models.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
|
File without changes
|