FastAPI教程 請(qǐng)求體 - 更新數(shù)據(jù)

2022-08-20 11:46 更新

用 PUT 更新數(shù)據(jù)

更新數(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ù)。

關(guān)于更新數(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。

用 PATCH 進(jìn)行部分更新

HTTP PATCH 操作用于更新 部分 數(shù)據(jù)。

即,只發(fā)送要更新的數(shù)據(jù),其余數(shù)據(jù)保持不變。

筆記

PATCH 沒有 PUT 知名,也怎么不常用。

很多人甚至只用 PUT 實(shí)現(xiàn)部分更新。

FastAPI 對(duì)此沒有任何限制,可以隨意互換使用這兩種操作。

但本指南也會(huì)分別介紹這兩種操作各自的用途。

使用 Pydantic 的 exclude_unset 參數(shù)

更新部分?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

使用 Pydantic 的 update 參數(shù)

接下來,用 .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

更新部分?jǐn)?shù)據(jù)小結(jié)

簡(jiǎn)而言之,更新部分?jǐn)?shù)據(jù)應(yīng):

  • 使用 PATCH 而不是 PUT (可選,也可以用 PUT);
  • 提取存儲(chǔ)的數(shù)據(jù);
  • 把數(shù)據(jù)放入 Pydantic 模型;
  • 生成不含輸入模型默認(rèn)值的 dict (使用 exclude_unset 參數(shù));只更新用戶設(shè)置過的值,不用模型中的默認(rèn)值覆蓋已存儲(chǔ)過的值。
  • 為已存儲(chǔ)的模型創(chuàng)建副本,用接收的數(shù)據(jù)更新其屬性 (使用 update 參數(shù))。
  • 把模型副本轉(zhuǎn)換為可存入數(shù)據(jù)庫(kù)的形式(比如,使用 jsonable_encoder)。這種方式與 Pydantic 模型的 .dict() 方法類似,但能確保把值轉(zhuǎn)換為適配 JSON 的數(shù)據(jù)類型,例如, 把 datetime 轉(zhuǎn)換為 str 。
  • 把數(shù)據(jù)保存至數(shù)據(jù)庫(kù);
  • 返回更新后的模型。
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é)中的思路。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)