프레임워크/FastAPI

[FastAPI] FastAPI [23] Body - Updates

:) :) 2023. 5. 23. 11:10

https://fastapi.tiangolo.com/tutorial/body-updates/

 

Body - Updates - FastAPI

Body - Updates Update replacing with PUT To update an item you can use the HTTP PUT operation. You can use the jsonable_encoder to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting datetime to s

fastapi.tiangolo.com

<FastAPI 공식문서 참조>

 

1. Update replacing with  PUT 

item을 Update하는 방법을 알아보자.

HTTP PUT 연산을 이용한다.

 

아래 예시는 PUT 연산을 이용한 update method 인데,

바로 이전 포스트에서 배운 jsonable_encoder를 이용해 datetime을 str로 바꾸는 과정까지 구현되어있다.

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str | None = None
    description: str | None = None
    price: float | None = None
    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": []},
}


@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
    return items[item_id]


@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
    update_item_encoded = jsonable_encoder(item)
    items[item_id] = update_item_encoded
    return update_item_encoded

이미 존재하는 데이터를 대체하기 위해 새로운 데이터를 받는데  PUT 연산이 사용된다.

 

1-1. Warning about replacing

 Replacing 할 때 주의해야할 점이 있다.

 

 다음 body를 포함한 내용을 PUT하여  bar 를 update하려는 경우,

{
    "name": "Barz",
    "price": 3,
    "description": None,
}

위 body는 bar에 이미 존재하는 속성  "tax" : 20.2  가 포함되어 있지 않기 때문에,

default value로 지정된  "tax" : 10.5  가 들어가게 된다.

 

tax의 20.2라는 값을 수정하려는 의도는 없었는데, 10.5로 의도치않게 수정해버릴 수도 있다.

 

 

2. Partial updates with PATCH

 위와 같은 문제 상황에서!! PATCH를 사용해 부분적으로 update할 수 있다.

 

* PUT만 이용해도 부분적으로 수정 가능하다. 따라서 사실 PATCH는 partial update에 범용적으로 사용되는 메소드는 아니다.

 

 

2-1. Using Pydantic's exclude_unset parameter

 Pydantic model의 .dict() 에서 부분 수정을 받으려면 exclude_unset 매개변수를 사용하는것은 매우 유용하다.

이렇게 사용한다.  item.dict(exclude_unset=True) 

 

기본 값을 제외하고 item model을 생성할 때 설정된 데이터만으로 dictionary가 생성됨을 의미한다.

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str | None = None
    description: str | None = None
    price: float | None = None
    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": []},
}


@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
    return items[item_id]


@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
    stored_item_data = items[item_id]
    stored_item_model = Item(**stored_item_data)
    update_data = item.dict(exclude_unset=True)
    updated_item = stored_item_model.copy(update=update_data)
    items[item_id] = jsonable_encoder(updated_item)
    return updated_item

 

 

2-2. Using Pydantic's update parameter

 .copy() 를 사용해 이미 존재하는 모델의 복사본을 만들어보자.

그 후 수정할 데이터를 담은 dict와 함께 update 매개변수를 넣어주면

*[코드는 다음과 같다. stored_item_model.copy(update=update_data): ]

 

from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str | None = None
    description: str | None = None
    price: float | None = None
    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": []},
}


@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
    return items[item_id]


@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
    stored_item_data = items[item_id]
    stored_item_model = Item(**stored_item_data)
    update_data = item.dict(exclude_unset=True)
    updated_item = stored_item_model.copy(update=update_data)
    items[item_id] = jsonable_encoder(updated_item)
    return updated_item

 

 

3. Partial updates recap

 요약해서, 부분 업데이트를 하려면 :

  • (선택적) PATCH를 PUT 대신에 사용한다.
  • 저장된 데이터를 돌려받는다.
  • Pydantic model 속의 데이터를 PUT 한다.
  • input model의 default value 없이 생성한다( exclude_unset  사용)
    • 이 방법은 이미 저장된 값을 overriding하는 대신 사실상 사용자가 설정한 값만 update할 수 있다.
  • 저장된 모델의 복사본을 만들고, 받은 partial updates들을 가지고 속성을 update한다(using the  update  parameter).
  • 복사된 모델을 DB에 저장할 수 있는 형태로 변환한다(jsonable_encoder를 사용)
  • 데이터를 DB에 저장한다.
  • 수정된 모델을 return.
  •  
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str | None = None
    description: str | None = None
    price: float | None = None
    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": []},
}


@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
    return items[item_id]


@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
    stored_item_data = items[item_id]
    stored_item_model = Item(**stored_item_data)
    update_data = item.dict(exclude_unset=True)
    updated_item = stored_item_model.copy(update=update_data)
    items[item_id] = jsonable_encoder(updated_item)
    return updated_item

 

아래를 집중해서 보자.

@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
    stored_item_data = items[item_id]
    stored_item_model = Item(**stored_item_data)
    update_data = item.dict(exclude_unset=True)
    updated_item = stored_item_model.copy(update=update_data)
    items[item_id] = jsonable_encoder(updated_item)
    return updated_item

 

 

4. Reference

https://fastapi.tiangolo.com/tutorial/body-updates/

 

Body - Updates - FastAPI

Body - Updates Update replacing with PUT To update an item you can use the HTTP PUT operation. You can use the jsonable_encoder to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting datetime to s

fastapi.tiangolo.com