본문 바로가기
카테고리 없음

FastAPI - Study 6일차 (Pydantic#2)

by 고래(부와 성공) 2024. 12. 28.

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\"]}"