FastAPI - 08 (응답 모델: Response Model)
출처: https://fastapi.tiangolo.com/tutorial/response-model/
아래의 내용은 공식 사이트의 내용을 제 경험과 생각을 추가하여 다시 정리한 것 입니다.
응답 모델 - Return Type
반환 되는 유형을 지정 하는 방법에 대한 예시 입니다.
함수 Return Type에 반환 유형 주석 사용
from typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = []
@app.post("/items/")
# -> 반환 유형 주석 사용, Item을 반환
async def create_item(item: Item) -> Item:
return item
@app.get("/items/")
# -> 반환 유형 주석 사용, List[Item]을 반환
async def read_items() -> List[Item]:
return [
Item(name="Portal Gun", price=42.0),
Item(name="Plumbus", price=32.0),
]
http://localhost:8000/items/
위의 url로 접속 하면 아래와 같은 결과가 나옵니다.
[
{
"name": "Portal Gun",
"description": null,
"price": 42.0,
"tax": null,
"tags": []
},
{
"name": "Plumbus",
"description": null,
"price": 32.0,
"tax": null,
"tags": []
}
]
만약 위의 소스를 아래와 같이 수정 하여 List[Item]을 반환 하지 않고 문자열을 반환 하도록 수정 합니다.
# ...
# 기존 소스
@app.get("/items/")
async def read_items() -> List[Item]: # -> 타입 주석 사용, List[Item]을 반환
# return [
# Item(name="Portal Gun", price=42.0),
# Item(name="Plumbus", price=32.0),
# ]
return "Hello World"
서비스를 다시 실행 시키고 아래의 URL로 접속 해 봅니다.
http://localhost:8000/items/
아래와 같이 에러가 발생 합니다.
Internal Server Error
서버의 로그를 확인 해 보면 아래와 같은 내용이 있습니다.
fastapi.exceptions.ResponseValidationError: 1 validation errors:
{'type': 'model_attributes_type', 'loc': ('response',), 'msg': 'Input should be a valid dictionary or object to extract fields from', 'input': 'Hello World!', 'url': 'https://errors.pydantic.dev/2.4/v/model_attributes_type'}
response_model 매개변수 사용
아래과 같이 경로 연산자에 response_model
매개변수를 사용하여 반환되는 유형을 지정 할 수 있습니다.
from typing import Any, List, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = []
# 경로 연산자에 response_model 매개변수로 반환되는 유형(Item)을 지정
@app.post("/items/", response_model=Item)
# 반환 유형 주석으로 Any를 사용하여 모든 유형을 반환
async def create_item(item: Item) -> Any:
return item
# 경로 연산자에 response_model 매개변수로 반환되는 유형(List[Item])을 지정
@app.get("/items/", response_model=List[Item])
# 반환 유형 주석으로 Any를 사용하여 모든 유형을 반환
async def read_items() -> Any:
return [
{"name": "Portal Gun", "price": 42.0},
{"name": "Plumbus", "price": 32.0},
]
response_model 매개변수를 사용 할 수 있는 경로 연산자
- @app.get()
- @app.post()
- @app.put()
- @app.delete()
- 등.
출력 유형 설정
출력 유형을 별도로 설정 할 수 있습니다.
입력과 동일 유형 반환
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserIn(BaseModel):
username: str
password: str
email: str
full_name: Union[str, None] = None
# Don't do this in production!
@app.post("/user/")
# 입력과 동일한 유형(UserIn)을 반환
async def create_user(user: UserIn) -> UserIn:
return user
위의 코드에서 반환되는 결과를 확인 하기 위한 테스트 코드(test.py) 입니다.
# 만얀 requests가 설치 되어 있지 않다면 아래의 명령어로 설치 합니다.(CMD 또는 shell )
# pip install requests
import requests
def test_create_user():
url = "http://localhost:8000/user/"
data = {
"username": "testuser",
"password": "testpassword",
"email": "test@example.com",
"full_name": "Test User"
}
response = requests.post(url, json=data)
print("Status Code:", response.status_code)
print("Response Body:", response.json())
if __name__ == "__main__":
test_create_user()
CMD 또는 Shell에서 아래의 명령어로 테스트 코드를 실행 합니다.
python test.py
# ubuntu
# python3 test.py
UserIn 유형이 Json으로 반환 되는 것을 확인 할 수 있습니다.
입력과 다른 유형 반환
from typing import Any, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserIn(BaseModel): # 입력 유형
username: str
password: str
email: str
full_name: Union[str, None] = None
class UserOut(BaseModel): # 출력 유형
username: str
# password는 출력하지 않음
email: str
full_name: Union[str, None] = None
# response_model로 출력 유형을 지정
@app.post("/user/", response_model=UserOut)
# 입력 타입은 UserIn
async def create_user(user: UserIn) -> Any:
return user
테스트 코드는 위와 동일합니다.
import requests
def test_create_user():
url = "http://localhost:8000/user/"
data = {
"username": "testuser",
"password": "testpassword",
"email": "test@example.com",
"full_name": "Test User"
}
response = requests.post(url, json=data)
print("Status Code:", response.status_code)
print("Response Body:", response.json())
if __name__ == "__main__":
test_create_user()
테스트 코드를 실행 합니다.
python test.py
UserOut(password가 없는) 유형이 Json으로 반환 되는 것을 확인 할 수 있습니다.
Status Code: 200
Response Body: {'username': 'testuser', 'email': 'test@example.com', 'full_name': 'Test User'}
위와 동일한 다른 코드 예시
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class BaseUser(BaseModel):
username: str
email: str
full_name: Union[str, None] = None
# 입력 유형
# BaseUser를 상속 받고 password를 추가
class UserIn(BaseUser):
password: str
@app.post("/user/")
# 입력 유형: UserIn, 출력 유형: BaseUser
async def create_user(user: UserIn) -> BaseUser:
return user
기타 반환 유형
Response로 반환 유형 설정
from fastapi import FastAPI, Response # Response 추가
# JSONResponse와 RedirectResponse 추가
from fastapi.responses import JSONResponse, RedirectResponse
app = FastAPI()
@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response: # Response 반환
if teleport:
# RedirectResponse로 리다이렉트
return RedirectResponse(url="https://www.youtube.com/watch")
# JSONResponse로 JSON 반환
return JSONResponse(content={"message": "Here's your interdimensional portal."})
테스트(teleport가 False인 경우)
http://localhost:8000/portal
아래와 같이 JSONResponse로 JSON이 반환 됩니다.
{"message":"Here's your interdimensional portal."}
테스트(teleport가 True인 경우)
http://localhost:8000/portal?teleport=true
RedirectResponse로
https://www.youtube.com/
로 리다이렉트 됩니다.
Response 하위 클래스로 반환 유형 설정
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/teleport")
# Response 하위 클래스인 RedirectResponse를 반환
async def get_teleport() -> RedirectResponse:
return RedirectResponse(url="https://www.youtube.com/")
위의 소스는 Response 하위 클래스(RedirectResponse) 반환 타입 주석을 설정 했으므로 정상적으로 작동 합니다.
응답 모델 인코딩 매개변수
응답 모델에서 기본값으로 설정되지 않은 필드를 응답에서 제외 할 수 있습니다.
from typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
# response_model_exclude_unset=True로 설정하여
# Key가 설정되지 않은 항목은 제외하고 반환
# 필수 항목이 없으면 오류가 발생
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]
테스트
http://localhost:8000/items/foo
결과
{"name":"Foo","price":50.2}
해시태그: #fastapi #uvicorn #fastapi Response Model #fastapi return type #fastapi response_model #fastapi response type #fastapi response
댓글남기기