3. 중첩된 모델
Pydantic에서의 중첩된 모델이란 하나의 모델이 다른 모델을 포함하는 구조이다.
이런 구조는 복잡한 데이터 형태를 모델링할 때 유용하다.
아래의 예제에서는, Item 클래스는 Image 클래스를 포함하고 있다.
Item 모델을 보면 image: Image로 정의되어 있는데,
이는 Item 모델이 Image 타입의 image 필드를 가지는 것이다.
# main.py로 작성
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI() # FastAPI 인스턴스를 생성한다.
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str
image: image
@app.get("/")
def read_root():
return {'message':'Hello World!!'}
@app.post("/items/")
async def crate_item(item: Item):
return {"item": item}
● class Image(BaseModel) : 이미지에 대한 정보를 담은 Pydantic 모델을 정의 (url, name 필드)
● class Item(BaseMode) : 아이템에 대한 정보를 담는 Pydantic 모델을 정의 (name, description, image 필드)
[curl 테스트]
]# curl -X POST "http://127.0.0.1:8000/items/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"name\": \"Snowman\", \"description\": \"big Size\", \"image\": {\"url\": \"http://test.com/image.jpg\", \"name\": \"test_url\"}}"
[중첩된 모델 사용의 장점]
● 재사용성 : Image 모델을 여러 다른 모델에서 사용가능
● 가독성 : 복잡한 데이터 구조를 좀 더 읽기 편하게 만듦
● 유지보수 용이 : 나중에 Image 모델을 업데이트 하더라도, 그 모델을 사용하는 모든 부분은 자동 업데이트됨
4. List와 Union
List와 Union은 복잡한 데이터 구조와 다형성을 모델링할 때 유용한 타입 힌트이다.
● List : List[<type>] 형식을 사용하여 지정된 <type>의 여러 값을 갖는 배열이나 리스트를 나타낸다.
Pydantic 모델에서 리스트를 사용하면 리스트 내 각 아이템에 대해 정의된 타입의 유효성이 검사된다.
● Union : Union[<type1>, <type2>, ...] 형식으로 여러 타입 중 하나를 허용하는 변수를 저으이할 수 있다.
예를 들면 Union[int, str]은 해당 필드가 정수 또는 문자열일 수 있음을 나타낸다.
# main.py로 작성
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Union
app = FastAPI() # FastAPI 인스턴스를 생성한다.
class Item(BaseModel):
name: str
tags: List[str]
variant: Union[int, str]
@app.get("/")
def read_root():
return {'message':'Hello World!!'}
@app.post("/items/")
async def crate_item(item: Item):
return {"item": item}
[curl 테스트]
]# curl -X POST "http://127.0.0.1:8000/items/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"name\": \"OS\", \"tags\": [\"Linux\", \"Windows\"], \"variant\": \"Server\"}"
위 curl 테스트에서 varaint 필드는 문자열과 정수형 둘다 Pydantic 유효성 검사에 이상 없음을 알수 있네요
■ 제네릭 타입
제네릭 타입은 일종의 '타입 템플릿'이다. 그래서 List[T] 같은 형태로 사용한다.
여기서 T는 Type variable(타입 변수)의 약자로 '아무 타입'이나 올 수 있다.
그래서 제네릭 타입을 사용하면 여러 다른 타입에 대해 동일한 로직을 적용받는다.
FastAPI와 Pydantic에서 제네릭 타입을 사용하려면 typing 모듈의 TypeVar와 Generic 클래스를 이용한다.
● TypeVar : TypeVar는 타입 변수를 생성하며, 제네릭 클래스나 함수가 사용할 수 있는 타입 매개변수를 정의한다.
TypeVar는 정적 타입 검사 도구가 타입 정보를 이해하고 검사할 수 있게 만드는 역할을 한다.
제네릭 타입을 사용하려면 먼저 타입 변수를 명시적으로 선언해야 하며, 그것이 바로 TypeVar의 역할이다.
TypeVar를 선언할 때는 일반적으로 변수 이름과 같은 문자열을 인자로 전달한다.
예를 들어 T = TypeVar('T')와 같이 작성한다.
● Generic[T]: Generic[T]는 T를 타입 매개변수로 가지는 제네릭 클래스를 정의할 때 사용한다.
T = TypeVar('T')에서 사용되는 T는 제네릭 프로그래밍의 관례를 따르는 표현이다.
여기서 T는 타입 변수(type variable)를 정의할 때 사용하는 이름이다.
● T (왼쪽)
타입 변수의 이름으로, 코드 내에서 타입 힌트로 사용된다.
예를 들어, Generic[T] 또는 List[T]와 같이 실제 코드에서 제네틱 타입으로 사용될 때 참조하는 이름이다.
● 'T' (오른쪽)
TypeVar 함수에 전달되는 문자열 리터럴로, TypeVar 객체를 생성할 때 내부적으로 사용되는 식별자이다.
파이썬의 타입 시스템과 관련된 도구들이 타입 정보를 처리할 때 이 문자열을 사용하여 타입 변수를 식별하게 된다.
즉, 왼쪽 T는 타입 변수를 코드 내에서 사용하기 위한 식별자이고, 오른쪽 'T'는 그 타입 변수를 내부적으로 구별하기 위한 문자열이다. 이 구분은 타입 체크 도구나 런타임이 아닌 타입 힌트를 분석할 때 중요한 역할을 한다.
TypeVar를 정의할 때 오른쪽에 전달하는 문자열 'T'는 문서화와 가독성을 위한 것이고, 이 문자열이 타입 체커에 의해 사용되는 실제 타입 변수의 이름이 된다.
왼쪽에 사용된 T는 이후 코드에서 해당 타입 변수를 참조할 때 사용되는 이름이다.
이처럼 T가 아닌 V, W, X 등 다른 변수 이름을 사용할 수도 있지만, T는 제네릭 타입의 'Type'을 나타내는 전통적인 이름으로 사용되고 있다.
# main.py로 작성
from fastapi import FastAPI
from pydantic import BaseModel
from typing import TypeVar, Generic
app = FastAPI() # FastAPI 인스턴스를 생성한다.
# T는 제네릭 타입에서 사용될 타입변수임
T = TypeVar('T')
# GenericItem 클래서는 제네릭 타입 T를 사용하는 모델이다.
# 이 모델은 다양한 타입의 'content'필드를 가질 수 있게 한다.
class GenericItem(BaseModel, Generic[T]):
name: str # 아이템의 이름 필드
content: T # 제네릭 타입을 사용하는 아이템의 내용 필드
@app.get("/")
def read_root():
return {'message':'Hello World!!'}
@app.post("/generic_items/")
def crate_item(item: GenericItem):
return {"item": item}
=> 이 코드에서 GenericItem 클래스는 name필드와 함께 타입이 T인 content 필드를 가지고 있다.
아래의 예는 배열, 문자열, 정수를 전부 사용하여 본 케이스이다.
만약 create_item() 함수 다음줄에 있는 GenericItem을 GenericItem[int]와 같이 int 타입으로 수정하면 인스턴스화 단계에 적용되어 content 필드에 정수 타입만을 받을 수 있게 된다.
즉 맨 아래에 예시만 문제없이 출력이 된다.
[curl 테스트]
curl -X POST "http://127.0.0.1:8000/generic_items/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"name\": \"Generic Test\", \"content\": [\"Linux\", \"Windows\"]}"