https://fastapi.tiangolo.com/tutorial/body-nested-models/
请求体-嵌套模型
使用FastAPI,您可以定义,验证,记录和使用任意深度嵌套的模型(这要归功于Pydantic)。
List fields
您可以将属性定义为子类型。例如,Python list
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from typing import Optional
from fastapi import FastAPI from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = 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
被定义为item里面的一个列表。
具有类型参数的List fields
Python使用一种特定的方式来声明具有内部类型或“类型参数”的列表:
从typing
导入 List
首先,List
从标准Python typing
模块导入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from typing import List, Optional
from fastapi import FastAPI from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = 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
声明一个有类型参数(子类型)的types,如list
,dict
,tuple
:
- 从
typing
模块导入它们 - 使用方括号将内部类型作为“类型参数”传递:
[
和 ]
1 2 3
| from typing import List
my_list: List[str]
|
这就是Python下面所有类型声明的标准语法。
所有具有内部类型的模型属性都使用相同的标准语法。
因此,在我们的示例中,tags
就是一个“字符串列表”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from typing import List, Optional
from fastapi import FastAPI from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = 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
|
集合类型
但是,理论上"tags
"不应该重复,它们可能是独一无二的。
这个时候set
出现了,它是Python
用于一组唯一项的特殊数据类型。
然后我们可以导入Set
并声明tags
为具有str
类型的set
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from typing import Optional, Set
from fastapi import FastAPI from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = 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
|
这样,即使收到带有重复数据的请求,该请求也将被转换为一组唯一的item。
即使源重复,每当输出该数据时,它们也将作为一组唯一项输出。
它也将相应地被注释/记录。
嵌套模型
Pydantic model 的每个属性都有一个类型。
但是该类型本身可以是另一种Pydantic model 。
因此,您可以使用特定的属性名、类型和验证来声明深度嵌套的JSON“对象”。
这些都是可以任意嵌套的。
定义一个子模型
例如,我们可以定义一个Image
模型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from typing import Optional, Set
from fastapi import FastAPI from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel): url: str name: str
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None tags: Set[str] = [] image: Optional[Image] = None
@app.put("/items/{item_id}") async def update_item(item_id: int, item: Item): results = {"item_id": item_id, "item": item} return results
|
使用子模型作为类型
然后我们可以将其用作属性的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from typing import Optional, Set
from fastapi import FastAPI from pydantic import BaseModel
app = FastAPI()
class Image(BaseModel): url: str name: str
class Item(BaseModel): name: str description: Optional[str] = None price: float tax: Optional[float] = None tags: Set[str] = [] image: Optional[Image] = 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将期望类似于以下内容的请求体:
1 2 3 4 5 6 7 8 9 10 11
| { "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进行声明即可:
- 编辑器支持(completion, etc),哪怕是嵌套模型
- 数据转换
- 数据验证
- 自动文档
特殊类型和验证
除了str、int、float等普通的单数类型外,还可以使用从str继承的更复杂的单数类型。
要查看所有选项,请查看Pydantic’s exotic types。您将在下一章中看到一些示例。
例如,在Image
模型中,我们有一个url
字段,我们可以将其声明为一个不同的str
,Pydantic的HttpUrl
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from typing import Optional, 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: Optional[str] = None price: float tax: Optional[float] = None tags: Set[str] = [] image: Optional[Image] = 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
等的子类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| from typing import List, Optional, 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: Optional[str] = None price: float tax: Optional[float] = None tags: Set[str] = [] images: Optional[List[Image]] = 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请求体,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| { "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
现在是一个image对象的列表
深层嵌套模型
您可以定义任意深度嵌套的模型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| from typing import List, Optional, 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: Optional[str] = None price: float tax: Optional[float] = None tags: Set[str] = [] images: Optional[List[Image]] = None
class Offer(BaseModel): name: str description: Optional[str] = None price: float items: List[Item]
@app.post("/offers/") async def create_offer(offer: Offer): return offer
|
offer
有一个item
的列表,而item
也有一个可以为空的image
列表
Bodies of pure lists
如果您期望JSON请求体的根节点JSON array
(Python list
),则可以在函数的参数中声明类型,与Pydantic模型相同:
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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模型,则无法获得这种编辑器支持。
而且,传入的dict会自动转换,输出也会自动转换为JSON。
任意dictS
的实体
您还可以将一个主体声明为具有某种类型的键和其他类型的值的dict
。
不必事先知道什么是有效的字段/属性名(Pydantic模型就是这样)。
如果您想接收未定义的keys
,这将非常有用。
当你想要其他类型的keys,例如int
的时候。
我们需要看一下下面这段代码。
在这种情况下,你可以接受任何key为int
,value是float
类型的dict
:
1 2 3 4 5 6 7 8 9 10
| 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
作为keys
。
但是Pydantic具有自动数据转换功能。
这意味着,即使API客户机只能将字符串作为keys
发送,但是只要这些字符串包含纯整数,Pydantic就可以转换并验证它们。
但是你接收到的字典,必须包含int类型的key和float类型的value。
总结
使用**FastAPI,**您将拥有Pydantic模型提供的最大灵活性,同时可以使您的代码简单,简短,美观。
可以得到这些好处:
- 编辑器支持(无处不在!)
- 数据转换(又名解析/序列化)
- 数据验证
- Schema文档
- 自动文档