python:Fastapi 请求体-嵌套模型
请求体 - 字段
字段与使用 Query
、Path
和 Body
在路径操作函数中声明额外的校验和元数据的方式相同,可以使用 Pydantic
的 Field
在 Pydantic
模型内部声明校验和元数据。
注意点:Field
是直接从 pydantic
导入的,而不是像其他的(Query
,Path
,Body
等)都从 fastapi
导入。
首先导入Field
:
from pydantic import Field
其次开始声明模型定义字段:
from pydantic import Field, BaseModel
class Items(BaseModel):
name: str
full_name: Optional[str] = Field(None, min_length=2)
description: Optional[str] = Field(None, max_length=100)
price: float = Field(..., gt=0)
gender: str = Field(..., alias="g")
tax: int = Field(None, title="用于文档中", description="在文档中显示")
注释信息:
min_length=2
是设置字段的最小长度max_length=100
是设置字段最大长度gt=0 是大于0
[ge
大于等于、lt
小于、le
小于等于]alias="g"
是取别名title="用于文档中"
显示在生成的docs
文档中description="在文档中显示"
显示在生成的docs
文档中
然后开始定义接口:
from typing import Optional
from fastapi import FastAPI
from fastapi import Body
from pydantic import Field, BaseModel
class Items(BaseModel):
name: str
full_name: Optional[str] = Field(None, min_length=2)
description: Optional[str] = Field(None, max_length=100)
price: float = Field(..., gt=0)
gender: str = Field(..., alias="g")
tax: int = Field(None, title="用于文档中", description="在文档中显示")
app = FastAPI()
@app.put("/items/{item_id}")
def read_field(item_id: int, item: Items = Body(..., embed=True)):
return {"item_id": item_id, "item": item}
然后启动服务:
lifeng@192 fastapiProject % uvicorn field_main:app --reload
INFO: Will watch for changes in these directories: ['/Users/lifeng/python-projects/python-code/fastapiProject']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [45277] using statreload
INFO: Started server process [45279]
INFO: Waiting for application startup.
INFO: Application startup complete.
WARNING: StatReload detected file change in 'field_main.py'. Reloading...
INFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [45279]
INFO: Started server process [45376]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: 127.0.0.1:55549 - "PUT /items/1 HTTP/1.1" 200 OK
最后请求接口:
PUT :http://127.0.0.1:8000/items/1
传入请求参数:
{
"item": {
"name": "haha",
"price": 2.8,
"g": "eee"
}
}
返回响应结果:
{
"item_id": 1,
"item": {
"name": "haha",
"full_name": null,
"description": null,
"price": 2.8,
"g": "eee",
"tax": null
}
}
请求体 - 嵌套模型
就是给属性(字段)定义为拥有子元素的类型,如:name: list
或 name: List
),还有就是嵌套子模型(可以直接理解为字段又嵌套一个子字段),如:image: Optional[Image] = None
等。
from fastapi import FastAPI
from typing import Optional, List, Set
from pydantic import BaseModel, Field
class Image(BaseModel):
url: str
name: str
class User(BaseModel):
name: str
full_name: Optional[str] = Field(None)
age: int = Field(..., ge=0)
tags: List[str] = []
email: Set[str] = set()
image: Image
app = FastAPI()
@app.put("/items/{item_id}")
def read_items(item_id: int, user: User):
results = {"item_id": item_id, "user": user}
return results
上述例子User
就是声明的模型,Image
也是声明的模型,只是Image
用在User
中,故被称为子模型(嵌套模型),是image
属性(字段)拥有的子元素的类型
tags
和email
是属性含子类型的类型image
属于子模型用作类型email
属性定义为集合的原因,是可以过滤重复值,保证唯一值,但是在传参时,按列表类型传即可
请求接口:
PUT :http://127.0.0.1:8000/items/1
请求参数:
{
"name": "haha",
"age": 11,
"tags": [
"1",
"2"
],
"email": [
"1",
"2"
],
"image": {
"url": "http://example.com/baz.jpg",
"name": "heihei"
}
}
请求结果:
{
"item_id": 1,
"user": {
"name": "haha",
"full_name": null,
"age": 11,
"tags": [
"1",
"2"
],
"email": [
"1",
"2"
],
"image": {
"url": "http://example.com/baz.jpg",
"name": "heihei"
}
}
}
如果你遇到字段需要定义为url
,就像这样的:
from pydantic import BaseModel
class Image(BaseModel):
url: str
name: str
那就直接把url
声明为HttpUrl
,而不是声明为str
:
from pydantic import BaseModel, HttpUrl
class Image(BaseModel):
url: HttpUrl
name: str
因为声明为HttpUrl
,该字符串将被检查是否为有效的 URL
,若检查出不是有效的URL
,则抛出异常:
{
"detail": [
{
"loc": [
"body",
"image",
"url"
],
"msg": "invalid or missing URL scheme",
"type": "value_error.url.scheme"
}
]
}
有些时候可能会遇到要在子参数中传数组,数组中要传多个参数,那这个时候就需要给子模型定义类型,也称子模型的属性:
from fastapi import FastAPI
from typing import List
from pydantic import BaseModel, Field, HttpUrl
class Image(BaseModel):
url: HttpUrl
name: str
class User(BaseModel):
name: str
age: int = Field(..., ge=0)
images: List[Image]
app = FastAPI()
@app.put("/items/{item_id}")
def read_items(item_id: int, user: User):
results = {"item_id": item_id, "user": user}
return results
请求接口:
PUT :http://127.0.0.1:8000/items/1
请求参数:
{
"name": "haha",
"age": 11,
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
},
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
},
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
}
]
}
请求结果:
{
"item_id": 1,
"user": {
"name": "haha",
"age": 11,
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
},
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
},
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
}
]
}
}
你还可以自定义任意深度的嵌套模型:
from fastapi import FastAPI
from typing import List, Optional, Set
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] = set()
images: List[Image]
class Offer(BaseModel):
name: str
description: Optional[str] = None
price: float
items: List[Item]
@app.post("/items/")
async def read_items(offer: Offer):
return offer
请求接口:
POST :http://127.0.0.1:8000/items
请求参数:
{
"name": "haha",
"price": 3.9,
"items": [
{
"name": "heihei",
"price": 4.99,
"tags": [
1,
2,
3,
3,
3,
3
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
}
]
}
]
}
请求结果:
{
"name": "haha",
"description": null,
"price": 3.9,
"items": [
{
"name": "heihei",
"description": null,
"price": 4.99,
"tax": null,
"tags": [
"1",
"3",
"2"
],
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "heihei"
}
]
}
]
}
从请求结果可以看到,请求参数中的tags
是一个数组,其实在接口中是一个集合,集合有去重功能,所以返回的结果自然就只有一个3
了。
今天先聊到这里吧,以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,直接私信留言会及时修正发布;非常期待你的一键 3 连【 点赞、收藏、分享 】哟,谢谢!
未完成,待续……
一直在努力,希望你也是!
微信搜索公众号:就用python