主體 - 巢狀模型¶
使用 FastAPI,您可以定義、驗證、記錄和使用任意深度巢狀的模型(感謝 Pydantic)。
列表欄位¶
您可以將屬性定義為子類型。例如,Python list
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: list = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
這將使 tags
成為一個列表,儘管它沒有宣告列表元素的類型。
具有類型參數的列表欄位¶
但 Python 有一種特殊的方法來宣告帶有內部類型的列表,或稱「類型參數」
導入 typing 的 List
¶
在 Python 3.9 及更高版本中,您可以使用標準的 list
來宣告這些類型註釋,如下所示。💡
但在 3.9 之前的 Python 版本(3.6 及更高版本)中,您首先需要從標準 Python 的 typing
模組導入 List
from typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
使用類型參數宣告 list
¶
要宣告具有類型參數(內部類型)的類型,例如 list
、dict
、tuple
- 如果您使用的是低於 3.9 的 Python 版本,請從
typing
模組導入其對應的版本 - 使用方括號
[
和]
將內部類型作為「類型參數」傳遞
在 Python 3.9 中,它會是
my_list: list[str]
在 3.9 之前的 Python 版本中,它會是
from typing import List
my_list: List[str]
這就是標準 Python 類型宣告的語法。
對具有內部類型的模型屬性使用相同的標準語法。
因此,在我們的範例中,我們可以使 tags
特定為「字串列表」
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: list[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import List, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = []
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
設定類型¶
但仔細想想,我們發現標籤不應該重複,它們可能是唯一的字串。
Python 有一個特殊的數據類型用於唯一項目的集合,即 set
。
然後我們可以將 tags
宣告為字串集合
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
這樣,即使您收到包含重複數據的請求,它也會被轉換為唯一項目的集合。
而且每當您輸出該數據時,即使來源有重複,它也會被輸出為唯一項目的集合。
並且它也會相應地被註釋/記錄。
巢狀模型¶
Pydantic 模型的每個屬性都有一個類型。
但該類型本身可以是另一個 Pydantic 模型。
因此,您可以使用特定的屬性名稱、類型和驗證來宣告深度巢狀的 JSON「物件」。
所有這些都可以任意巢狀。
定義子模型¶
例如,我們可以定義一個 Image
模型
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
使用子模型作為類型¶
然後我們可以將其用作屬性的類型
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
這表示 FastAPI 預期主體類似於
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}
同樣,只需進行該宣告,使用 FastAPI 即可獲得
- 編輯器支援(自動完成等),即使是巢狀模型
- 資料轉換
- 資料驗證
- 自動文件生成
特殊類型與驗證¶
除了像 str
、int
、float
等一般的單一類型之外,您還可以繼承自 str
的更複雜的單一類型。
若要查看所有可用的選項,請參考 Pydantic 的類型概觀。您將在下一章看到一些範例。
例如,在 Image
模型中,我們有一個 url
欄位,我們可以將其宣告為 Pydantic 的 HttpUrl
實例,而不是 str
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
image: Union[Image, None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
字串將會被檢查是否為有效的 URL,並在 JSON Schema / OpenAPI 中記錄。
具有子模型列表的屬性¶
您也可以使用 Pydantic 模型作為 list
、set
等的子類型。
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
images: Union[list[Image], None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
from typing import List, Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
images: Union[List[Image], None] = None
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results
這將會預期(轉換、驗證、記錄等)類似以下的 JSON 內容:
{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}
資訊
請注意 images
鍵現在如何具有圖片物件的列表。
深度巢狀模型¶
您可以定義任意深度巢狀的模型
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None
class Offer(BaseModel):
name: str
description: str | None = None
price: float
items: list[Item]
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: set[str] = set()
images: Union[list[Image], None] = None
class Offer(BaseModel):
name: str
description: Union[str, None] = None
price: float
items: list[Item]
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
from typing import List, Set, Union
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: Set[str] = set()
images: Union[List[Image], None] = None
class Offer(BaseModel):
name: str
description: Union[str, None] = None
price: float
items: List[Item]
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer
資訊
請注意 Offer
如何具有 Item
列表,而這些 Item
又具有可選的 Image
列表。
純列表的請求主體¶
如果您預期的 JSON 請求主體的頂層值是一個 JSON array
(一個 Python list
),您可以在函數的參數中宣告類型,就像在 Pydantic 模型中一樣
images: List[Image]
或在 Python 3.9 及更高版本中
images: list[Image]
如下所示
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
return images
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
url: HttpUrl
name: str
@app.post("/images/multiple/")
async def create_multiple_images(images: List[Image]):
return images
全面的編輯器支援¶
而且您在任何地方都可獲得編輯器支援。
即使是列表中的項目
如果您直接使用 dict
而不是 Pydantic 模型,就無法獲得這種編輯器支援。
但您也不必擔心它們,傳入的字典會自動轉換,您的輸出也會自動轉換為 JSON。
任意 dict
的請求主體¶
您也可以將請求主體宣告為具有某種類型鍵和某種類型值的 dict
。
這樣,您就不必事先知道有效的欄位/屬性名稱是什麼(就像使用 Pydantic 模型的情況一樣)。
如果您想要接收您還不知道的鍵,這會很有用。
另一個有用的情況是當您想要使用其他類型的鍵(例如,int
)。
這就是我們將在這裡看到的。
在這種情況下,只要 dict
具有 int
鍵和 float
值,您就可以接受任何 dict
from fastapi import FastAPI
app = FastAPI()
@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
return weights
from typing import Dict
from fastapi import FastAPI
app = FastAPI()
@app.post("/index-weights/")
async def create_index_weights(weights: Dict[int, float]):
return weights
提示
請記住,JSON 只支援 str
作為鍵。
但 Pydantic 具有自動資料轉換功能。
這表示,即使您的 API 用戶端只能傳送字串作為鍵,只要這些字串包含純整數,Pydantic 就會轉換並驗證它們。
而您作為 weights
接收的 dict
實際上將具有 int
鍵和 float
值。
摘要¶
使用 FastAPI,您可以獲得 Pydantic 模型提供的最大彈性,同時保持程式碼簡潔、簡短且優雅。
並享有所有好處
- 編輯器支援(任何地方都有程式碼自動完成!)
- 資料轉換(又稱剖析/序列化)
- 資料驗證
- Schema 文件
- 自動產生文件