更新數(shù)據(jù)請(qǐng)用 HTTP PUT 操作。
把輸入數(shù)據(jù)轉(zhuǎn)換為以 JSON 格式存儲(chǔ)的數(shù)據(jù)(比如,使用 NoSQL 數(shù)據(jù)庫(kù)時(shí)),可以使用 jsonable_encoder。例如,把 datetime 轉(zhuǎn)換為 str。
from typing import List, Optional
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = 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 用于接收替換現(xiàn)有數(shù)據(jù)的數(shù)據(jù)。
用 PUT 把數(shù)據(jù)項(xiàng) bar 更新為以下內(nèi)容時(shí):
{
"name": "Barz",
"price": 3,
"description": None,
}
因?yàn)樯鲜鰯?shù)據(jù)未包含已存儲(chǔ)的屬性 "tax": 20.2,新的輸入模型會(huì)把 "tax": 10.5 作為默認(rèn)值。
因此,本次操作把 tax 的值「更新」為 10.5。
HTTP PATCH 操作用于更新 部分 數(shù)據(jù)。
即,只發(fā)送要更新的數(shù)據(jù),其余數(shù)據(jù)保持不變。
筆記
PATCH 沒有 PUT 知名,也怎么不常用。
很多人甚至只用 PUT 實(shí)現(xiàn)部分更新。
FastAPI 對(duì)此沒有任何限制,可以隨意互換使用這兩種操作。
但本指南也會(huì)分別介紹這兩種操作各自的用途。
更新部分?jǐn)?shù)據(jù)時(shí),可以在 Pydantic 模型的 .dict() 中使用 exclude_unset 參數(shù)。
比如,item.dict(exclude_unset=True)。
這段代碼生成的 dict 只包含創(chuàng)建 item 模型時(shí)顯式設(shè)置的數(shù)據(jù),而不包括默認(rèn)值。
然后再用它生成一個(gè)只含已設(shè)置(在請(qǐng)求中所發(fā)送)數(shù)據(jù),且省略了默認(rèn)值的 dict:
from typing import List, Optional
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = 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
接下來,用 .copy() 為已有模型創(chuàng)建調(diào)用 update 參數(shù)的副本,該參數(shù)為包含更新數(shù)據(jù)的 dict。
例如,stored_item_model.copy(update=update_data):
from typing import List, Optional
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = 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
簡(jiǎn)而言之,更新部分?jǐn)?shù)據(jù)應(yīng):
from typing import List, Optional
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = 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
提示
實(shí)際上,HTTP PUT 也可以完成相同的操作。 但本節(jié)以 PATCH 為例的原因是,該操作就是為了這種用例創(chuàng)建的。
筆記
注意,輸入模型仍需驗(yàn)證。
因此,如果希望接收的部分更新數(shù)據(jù)可以省略其他所有屬性,則要把模型中所有的屬性標(biāo)記為可選(使用默認(rèn)值或 None)。
為了區(qū)分用于更新所有可選值的模型與用于創(chuàng)建包含必選值的模型,請(qǐng)參照更多模型 一節(jié)中的思路。
更多建議: