更新替换为PUT
为了更新一个item你可以使用HTTP PUT操作。
您可以使用jsonable_encoder将输入数据转换为可以存储为JSON的数据(例如,使用NoSQL数据库)。例如,将datetime转换为str。
from typing import Listfrom fastapi import FastAPIfrom fastapi.encoders import jsonable_encoderfrom pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: str = Nonedescription: str = Noneprice: float = Nonetax: float = 10.5tags: 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_encodedreturn update_item_encoded
关于替换警告
这意思是如果你想要使用带有请求体的PUT来更新bar这个item:
{"name": "Barz","price": 3,"description": None,}
因为它不包含已经存储的属性"tax":20.2,所以输入模型将采用“tax”的默认值:10.5。
并且数据将以“新”tax为10.5保存。
使用PATCH进行部分更新
您还可以使用HTTP PATCH操作来部分更新数据。
这意味着您只能发送要更新的数据,其余的保持不变。
:::tips
与PUT相比,PATCH不太常用和已知。
而且许多团队甚至仅对部分更新使用PUT。
您可以随意使用它们,FastAPI不受任何限制。
但是,本指南或多或少地向您展示了如何使用它们。
:::
使用Pydantic的exclude_unset参数
如果要接收部分更新,则在Pydantic模型的.dict()中使用参数exclude_unset非常有用。
像item.dict(exclude_unset=True)
这将生成仅包含创建item模型时设置的数据的dict,不包括默认值。
然后,您可以使用它生成仅包含设置的数据(在请求中发送)的dict,而忽略默认值:
from typing import Listfrom fastapi import FastAPIfrom fastapi.encoders import jsonable_encoderfrom pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: str = Nonedescription: str = Noneprice: float = Nonetax: float = 10.5tags: 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
使用Pydantic的update参数
现在,您可以使用.copy()创建现有模型的副本,然后将update参数与包含要更新数据的dict一起传递。
像`storeditemmodel.copy(update=update_data):
from typing import Listfrom fastapi import FastAPIfrom fastapi.encoders import jsonable_encoderfrom pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: str = Nonedescription: str = Noneprice: float = Nonetax: float = 10.5tags: 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
部分的更新概括
总之,要应用部分更新,您将:
(可选)使用PATCH而不是PUT。
检索存储的数据
把数据放到Pydantic的模型中
从输入模型生成一个不带默认值的字典(使用excludeunset)。 这样,您只能更新用户实际设置的值,而不是覆盖已经与默认值一起存储在模型中的值。 创建存储模型的副本,使用接收到的部分更新(使用update参数)更新其属性。 将复制的模型转换为可以存储在数据库中的内容(例如,使用jsonableencoder)。
这类似于再次使用模型的.dict()方法,但是它可以确保值(并将其转换为可以转换为JSON的数据类型,例如datetime到str)。
保存数据到你的数据库中。
返回一个更新过后的模型
from typing import Listfrom fastapi import FastAPIfrom fastapi.encoders import jsonable_encoderfrom pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: str = Nonedescription: str = Noneprice: float = Nonetax: float = 10.5tags: 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
:::tips
实际上,您可以将相同的技术用于HTTP PUT操作。
但是这里的示例使用PATCH,因为它是为这些用例创建的。
:::
:::tips
请注意,输入模型仍处于验证状态。
因此,如果要接收可以忽略所有属性的部分更新,则需要有一个模型,其中所有属性都标记为可选(默认值或无)。
要与具有所有可选更新值的模型和具有必需创建值的模型区分开,可以使用“附加模型”中所述的思想。
:::
