更新替换为PUT

为了更新一个item你可以使用HTTP PUT操作。
您可以使用jsonable_encoder将输入数据转换为可以存储为JSON的数据(例如,使用NoSQL数据库)。例如,将datetime转换为str。

  1. from typing import List
  2. from fastapi import FastAPI
  3. from fastapi.encoders import jsonable_encoder
  4. from pydantic import BaseModel
  5. app = FastAPI()
  6. class Item(BaseModel):
  7. name: str = None
  8. description: str = None
  9. price: float = None
  10. tax: float = 10.5
  11. tags: List[str] = []
  12. items = {
  13. "foo": {"name": "Foo", "price": 50.2},
  14. "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
  15. "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
  16. }
  17. @app.get("/items/{item_id}", response_model=Item)
  18. async def read_item(item_id: str):
  19. return items[item_id]
  20. @app.put("/items/{item_id}", response_model=Item)
  21. async def update_item(item_id: str, item: Item):
  22. update_item_encoded = jsonable_encoder(item)
  23. items[item_id] = update_item_encoded
  24. return update_item_encoded

PUT用于接收应替换现有数据的数据。

关于替换警告

这意思是如果你想要使用带有请求体的PUT来更新bar这个item:

  1. {
  2. "name": "Barz",
  3. "price": 3,
  4. "description": None,
  5. }

因为它不包含已经存储的属性"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,而忽略默认值:

  1. from typing import List
  2. from fastapi import FastAPI
  3. from fastapi.encoders import jsonable_encoder
  4. from pydantic import BaseModel
  5. app = FastAPI()
  6. class Item(BaseModel):
  7. name: str = None
  8. description: str = None
  9. price: float = None
  10. tax: float = 10.5
  11. tags: List[str] = []
  12. items = {
  13. "foo": {"name": "Foo", "price": 50.2},
  14. "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
  15. "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
  16. }
  17. @app.get("/items/{item_id}", response_model=Item)
  18. async def read_item(item_id: str):
  19. return items[item_id]
  20. @app.patch("/items/{item_id}", response_model=Item)
  21. async def update_item(item_id: str, item: Item):
  22. stored_item_data = items[item_id]
  23. stored_item_model = Item(**stored_item_data)
  24. update_data = item.dict(exclude_unset=True)
  25. updated_item = stored_item_model.copy(update=update_data)
  26. items[item_id] = jsonable_encoder(updated_item)
  27. return updated_item

使用Pydantic的update参数

现在,您可以使用.copy()创建现有模型的副本,然后将update参数与包含要更新数据的dict一起传递。
像`storeditemmodel.copy(update=update_data):

  1. from typing import List
  2. from fastapi import FastAPI
  3. from fastapi.encoders import jsonable_encoder
  4. from pydantic import BaseModel
  5. app = FastAPI()
  6. class Item(BaseModel):
  7. name: str = None
  8. description: str = None
  9. price: float = None
  10. tax: float = 10.5
  11. tags: List[str] = []
  12. items = {
  13. "foo": {"name": "Foo", "price": 50.2},
  14. "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
  15. "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
  16. }
  17. @app.get("/items/{item_id}", response_model=Item)
  18. async def read_item(item_id: str):
  19. return items[item_id]
  20. @app.patch("/items/{item_id}", response_model=Item)
  21. async def update_item(item_id: str, item: Item):
  22. stored_item_data = items[item_id]
  23. stored_item_model = Item(**stored_item_data)
  24. update_data = item.dict(exclude_unset=True)
  25. updated_item = stored_item_model.copy(update=update_data)
  26. items[item_id] = jsonable_encoder(updated_item)
  27. return updated_item

部分的更新概括

总之,要应用部分更新,您将:
(可选)使用PATCH而不是PUT。
检索存储的数据
把数据放到Pydantic的模型中
从输入模型生成一个不带默认值的字典(使用excludeunset)。 这样,您只能更新用户实际设置的值,而不是覆盖已经与默认值一起存储在模型中的值。 创建存储模型的副本,使用接收到的部分更新(使用update参数)更新其属性。 将复制的模型转换为可以存储在数据库中的内容(例如,使用jsonableencoder)。
这类似于再次使用模型的.dict()方法,但是它可以确保值(并将其转换为可以转换为JSON的数据类型,例如datetime到str)。
保存数据到你的数据库中。
返回一个更新过后的模型

  1. from typing import List
  2. from fastapi import FastAPI
  3. from fastapi.encoders import jsonable_encoder
  4. from pydantic import BaseModel
  5. app = FastAPI()
  6. class Item(BaseModel):
  7. name: str = None
  8. description: str = None
  9. price: float = None
  10. tax: float = 10.5
  11. tags: List[str] = []
  12. items = {
  13. "foo": {"name": "Foo", "price": 50.2},
  14. "bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
  15. "baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
  16. }
  17. @app.get("/items/{item_id}", response_model=Item)
  18. async def read_item(item_id: str):
  19. return items[item_id]
  20. @app.patch("/items/{item_id}", response_model=Item)
  21. async def update_item(item_id: str, item: Item):
  22. stored_item_data = items[item_id]
  23. stored_item_model = Item(**stored_item_data)
  24. update_data = item.dict(exclude_unset=True)
  25. updated_item = stored_item_model.copy(update=update_data)
  26. items[item_id] = jsonable_encoder(updated_item)
  27. return updated_item


:::tips 实际上,您可以将相同的技术用于HTTP PUT操作。
但是这里的示例使用PATCH,因为它是为这些用例创建的。 :::
:::tips 请注意,输入模型仍处于验证状态。
因此,如果要接收可以忽略所有属性的部分更新,则需要有一个模型,其中所有属性都标记为可选(默认值或无)。
要与具有所有可选更新值的模型和具有必需创建值的模型区分开,可以使用“附加模型”中所述的思想。 :::