TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 웹개발>

[파이썬] FastAPI - Pydantic (2)

by 기록자_Recordian 2024. 8. 17.
728x90
반응형
시작에 앞서
해당 내용은 <가장 빠른 풀스택을 위한 Flask & FastAPI>, Dave Lee 지음. BJ Public 출판.
내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.

이전 내용
 

[파이썬] FastAPI - HTTP 메서드, Pydantic

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용 [파이썬] FastAPI - 타입 힌트시작에 앞서해

puppy-foot-it.tistory.com

 

★ Pydantic 관련 상세 내용

 

 

[파이썬] Pydantic: 데이터 검증 라이브러리

Pydantic이해하기 쉬운 Pydantic: 데이터 유효성 검사와 데이터 모델링의 새로운 기준  Pydantic은 Python에서 가장 널리 사용되는 데이터 검증 라이브러리이다.Pydantic은 Python의 데이터 유효성 검

puppy-foot-it.tistory.com


Pydantic 필드 제약 조건

 

Field는 Pydantic 모델에서 필드에 추가적인 정보나 제약 조건을 지정할 때 사용하는 함수이다.

다양한 인자를 통해 세부 설정을 할 수 있으며, 주요 옵션은 아래와 같다.

  • default: 필드의 기본값을 지정. 만약 기본값이 없다면 필수 입력 필드가 된다.
  • alias: JSON 필드의 이름을 파이썬 변수와 다르게 지정할 때 사용
  • title: 스키마에서 볼 수 있는 추가적인 정보로, 주로 문서화에 사용
  • description: 필드에 대한 설명 추가. 주로 API 문서에서 확인
  • min_length & max_length: 문자열 길이의 최솟값과 최댓값 지정
  • gt(greater than), lt(less than): 숫자의 크기 제약 추가
  • regex: 정규 표현식을 통한 패턴 매칭

[Field를 사용한 예제 코드]

from fastapi import FastAPI
from pydantic import BaseModel, Field
from typing import List

app = FastAPI()

class Item(BaseModel):
    # 'name'은 최소 2자, 최대 50자를 가져아 하며 필수 필드
    name: str = Field(..., title="Item Name", min_length=2, max_length=50)
    
    # 'description'은 선택 필드이며, 최대 300자 까지 가능
    description: str = Field(None, description="The description of the item", max_length=300)
    
    # 'price'는 0보다 커야 하며 필수 필드
    price: float = Field(..., gt=0, description="The price must be greater than zero")
    
    # 'tag' 필드는 선택이며, 기본값으로 빈 리스트를 가짐. JSON에서는 'item-tags'로 나타냄
    tag: List[str] = Field(default=[], alias="item-tags")

@app.post("/items/")
async def create_item(item: Item):
    # 아이템 생성을 위한 엔드포인트로, 모델 인스턴스의 딕셔너리 표현 반환
	return {"item": item.dict()}

Field 테스트

 

curl로 테스트할 수 있는 다양한 케이스와 그 결과를 확인하며, Field 사용법을 이해해본다.

 

케이스 1. 정상적인 요청

curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"MyItem\", \"price\": 35.4}"

▶ 서버로부터 200 OK 응답을 받는다. (JSON 바디에 정의된 name과 price 필드가 Pydantic 모델의 필드 제약 조건 충족)

 

케이스 2. name 필드의 길이가 짧은 경우

curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"A\", \"price\": 35.4}"

{"detail":[{"type":"string_too_short","loc"["body","name"],"msg":"String should have at least 2 characters","input":"A","ctx":{"min_length":2},"url":"https://errors.pydantic.dev/2.8/v/string_too_short"}]}

▶ 422 Unprocessable Entity 오류 발생 (name 필드의 길이가 Pydantic 모델에서 설정한 min_length 조건 불만족 - 조건: 최소 2글자)

 

케이스3. price 필드가 0보다 작거나 같은 경우

curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"MyItem\", \"price\": -1}"

▶ 422 Unprocessable Entity 오류 발생 (price 필드의 값이 Pydantic 모델에서 설정한 gt(greater than) 조건 불만족 - 조건: 0이상)

 

케이스4. 필수 필드를 누락한 경우(name 누락)

curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"price\": 35.4}"

▶ 422 Unprocessable Entity 오류 발생  (필드가 누락되었다는 메시지 반환)

 

케이스5. 여러 제약 조건을 위반한 경우

curl -X POST "http://127.0.0.1:8000/items/" -H "Content-Type: application/json" -d "{\"name\":\"A\", \"price\": -1}"

▶ 422 Unprocessable Entity 오류 발생(name 2글자 이상 조건 불만족, price 1 이상 조건 불만족)


중첩된 모델

 

중첩된 모델이라는 것은

하나의 모델이 다른 모델을 포함하는 구조를 의미한다.

이러한 구조는 복잡한 데이터 형태를 모델링할 때 유용한데, 예를 들어 여기서 Item 클래스는 Image 클래스를 포함하고 있다.

Item 모델을 보면 image: image로 정의되어 있는데, 이는 Item 모델이 Image 타입의 image 필드를 가진다는 것을 의미한다.

from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class Image(BaseModel):
    url: str
    name: str
    
class Item(BaseModel):
    name: str
    description: str
    image: Image
    
@app.post("/items/")
def create_item(item: Item):
	return {"item": item.dict()}
  • class Image(BaseModel): 이미지에 대한 정보를 담는 Pydantic 모델 정의 (위 코드에서는 url과 name이라는 두 가지 필드 존재)
  • class Item(BaseModel): 아이템에 대한 정보를 담는 Pydantic 모델 정의 (위 코드에서는 name, description, image 라는 필드를 가지며, 특히 image 필드의 타입은 앞서 정의한 Image 클래스)
  • def create_item(item: Item): FastAPI 라우터에서 이 Item 모델 사용. 클라이언트로부터 전달받은 JSON 데이터가 Item 모델과 일치하는 지 자동으로 검사

▶ 이 예에서는 name, description, 그리고 중첩된 image 객체를 포함한 JSON 데이터를 전달한다. 이렇게 하면 FastAPI는 Item 모델을 기반으로 들어온 데이터의 유효성을 자동으로 검사하고, 응답으로 그 데이터를 반환한다.


List와 Union

 

List와 Union은 복잡한 데이터 구조와 다형성을 모델링할 때 유용한 타입 힌트이다.

  • List: List[<type>] 형식을 사용하여 지정된 <type>의 여러 값을 갖는 배열이나 리스트를 나타낸다. (예. List[int]: 정수들의 리스트 / List[str]: 문자열들의 리스트) ▶ Pydantic 모델에서 리스트를 사용하면 리스트 내 각 아이템에 대해 정의된 타입의 유효성이 검사된다.
  • Union: Union[<type1>, <type2>, ...] 형식으로 여러 타입 중 하나를 허용하는 변수를 정의할 수 있다. (예. Union[int, str]: 해당 필드가 정수 또는 문자열 일 수 있음)  ▶ Pydantic 모델은 제공된 값이 Union에 지정된 타입 중 하나와 일치하는 지 검사한다.

제네릭 타입

 

제네릭 타입은 일종의 '타입 템플릿'이다. 그래서 List[T] 같은 형태로 사용한다.

▶ T는 타입 변수(Type variable)라고 부르며, 여기서는 '아무 타입이나' 올 수 있음을 의미하여 여러 다른 타입에 대해 동일한 로직을 적용할 수 있다.

★ T는 단순한 변수일 뿐 의미 자체는 없어 대신에 다른 알파벳을 사용해도 되지만, 관례적으로 T를 많이 사용한다.

만약 두 개의 다른 타입 변수가 필요하다면 U = TypeVar('U') 등과 같이 다른 문자를 사용하여 선언할 수 있다.

 

FastAPI와 Pydantic 에서 제네릭 타입을 사용하려면 typing 모듈의 TypeVar와 Generic 클래스를 이용한다.

  • TypeVar: 타입 변수를 생성하며, 제네릭 크래스나 함수가 사용할 수 있는 타입 매개변수를 정의한다. 동적 타이핑 언어인 파이썬에서 TypeVar는 정적 타입 검사 도구가 타입 정보를 이해하고 검사할 수 있게 만드는 역할을 한다. 제네릭 타입을 사용하려면 먼저 타입 변수를 명시적으로 선언해야 하며, 그것이 바로 TypeVar의 역할이다. TypeVar를 선언할 때는 일반적으로 변수 이름과 같은 문자열을 인자로 전달한다.
T = TypeVar('T')

- 왼쪽의 T: 타입 변수의 이름으로, 코드 내에서 타입 힌트로 사용

- 오른쪽의 T: TypeVar 함수에 전달되는 문자열 리터럴. TypeVar 객체를 생성할 때 내부적으로 사용되는 식별자. 파이썬의 타입 시스템과 관련된 도구들(예. linters, IDEs) ▶ 문서화와 가독성을 위함.

  • Generic[T]: T를 타입 매개변수로 가지는 제네릭 클래스를 정의할 때 사용한다.

[Type Var와 Generic 타입에 대한 코드 예시]

from typing import TypeVar, Generic
from pydantic import BaseModel

# 타입 변수 T 선언 (커스텀 제네릭 타입을 만들기 위한 첫 단계)
# 'T'는 임의의 타입을 나타내는 타입 변수
T = TypeVar('T')

# Generic[T]를 상속받는 클래스를 정의함으로써, GenericItem은 어떤 타입 T도 받을 수 있는 제네릭 클래스가 된다
# 이 클래스는 이름(name)과 내용(content)을 필드로 가지며, content의 타입은 동적으로 결정됨.
class GenericItem(BaseModel, Generic[T]):
	name: str # 아이템의 이름 필드, 문자열 타입
    content: T # 아이템의 내용 필드, T 타입

▶ Pydantic과 FastAPI에서 제네릭 타입을 사용하면 데이터 모델의 유연성을 높이면서도 타입 안정성을 유지할 수 있다. 또, 다양한 데이터 타입에 대해 같은 로직을 적용할 수 있는 모델을 만들 수 있다.

 

[Pydantic 제네릭 타입 예시]

from typing import TypeVar, Generic
from pydantic import BaseModel
from fastapi import FastAPI

app = FastAPI()

# T는 제네릭 타입에서 사용될 변수
T = TypeVar('T')

# GenericItem 클래스는 제네릭 타입 T를 사용하는 모델
# 이 모델은 다양한 타입의 'content' 필드를 가질 수 있음
class GenericItem(BaseModel, Generic[T]):
	name: str # 아이템의 이름 필드
    content: T # 제네릭 타입을 사용하는 아이템의 내용 필드

# 이 엔드포인트는 정수 타입의 'content'를 가진 GenericItem 객체를 생성
@app.post("/generic_items/")
def create_item(item: GenericItem[int]):
	reutrn {"item": item.dict()}

 

◆ curl 테스트

curl -X POST "http://127.0.0.1:8000/generic_items/" -H "accept:application/json" -H "Content-Type: application/json" -d "{\"name\": \"Generic\", \"content\": 42}"

 

  • POST 메서드를 사용하여 /generic_items/ 엔드포인트에 데이터 전송
  • HTTP 헤더는 요청이 JSON을 받아들이고(JSON에 대한 accept)
  • JSON 형식의 데이터를 전송한다는 것을 나타냄(Content-Type: JSON)
  • 요청 바디(-d)에는 name과 content가 포함된 JSON 객체 전달
  • 서버에서는 GenericItem[int] 타입에 따라 content가 정수인지 검사하고 타입이 일치하면 요청된 데이터를 그대로 반환
  • 만약 정수가 아닌 다른 타입의 데이터를 보내면, Pydantic은 타입 에러를 반환하여 요청이 유효하지 않음을 알려줌

다음 내용

 

[파이썬] FastAPI - 응답 모델

시작에 앞서해당 내용은 , Dave Lee 지음. BJ Public 출판.내용을 토대로 작성되었습니다. 보다 자세한 사항은 해당 교재를 참고하시기 바랍니다.이전 내용 [파이썬] FastAPI - Pydantic (2)시작에 앞서해당

puppy-foot-it.tistory.com

 

728x90
반응형