请求体 - 字段
字段与使用?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
|