FastAPI教程I

本文参考FastAPI教程https://fastapi.tiangolo.com/zh/tutorial

第一步

import uvicorn
from fastapi import FastAPIapp = FastAPI()@app.get("/")
async def root():return {"message": "Hello World"}if __name__ == '__main__':uvicorn.run("test:app", host="127.0.0.1", port=8000, reload=True)
$ uvicorn main:app --reload
  • 导入FastAPIFastAPI是一个为你的API提供了所有功能的Python类
  • app = FastAPI()创建一个FastAPI实例,这个实例将是创建你所有API的主要交互对象。这个app同样在如下命令中被uvicorn所引用。
  • 创建一个路径操作。
    【路径】:这里的【路径】指的是从URL中第一个/起的后半部分,比如在https://example.com/items/foo中,路径是/items/foo,【路径】也通常被称为【端点】或【路由】。
    【操作】:这里的【操作】是指一种HTTP【方法】,如POST——创建数据,GET——读取数据,PUT——更新数据,DELETE——删除数据,以及更少见的几种OPTIONS,HEAD,PATCH,TRACE,在HTTP协议中,你可以使用以上的其中一种(或多种)【方法】与每个路径进行通信。
  • 定义一个路径操作装饰器@app.get("/")告诉FastAPI在它下方的函数负责处理如下访问请求:请求路径为/,使用get操作。(@something语法在Python中被称为【装饰器】,接收位于其下方的函数并且用它完成一些工作,这里是路径操作装饰器。)
  • 定义路径操作函数:位于路径操作装饰器下的函数,这里是async def root(),这个例子用的是async函数,其作用在后面讲。
  • 返回内容:可以返回一个dictlist,像strint一样的值,等等。还可以返回Pydanic模型(后面会说),还有许多其他将会自动转换为JSON的对象和模型(包括ORM对象等)。

查看运行结果

  • 打开浏览器访问 http://127.0.0.1:8000。讲看到如下的JSON相应:
{"message": "Hello World"}

交互式API文档

  • 跳转到 http://127.0.0.1:8000/docs。你将会看到自动生成的交互式API文档(由Swagger UI提供)。

可选的API文档

  • 前往 http://127.0.0.1:8000/redoc。你将会看到可选的自动生成文档(由ReDoc提供)。

路径参数

FastAPI支持以下路径模板语法定义动态路由,声明路径参数(变量)

import uvicorn
from fastapi import FastAPIapp = FastAPI()@app.get("/items/{item_id}/{go}")
async def read_item(go):return {"item_id": go}if __name__ == '__main__':uvicorn.run("test:app", host="127.0.0.1", port=8000, reload=True)

这段代码声明了相应位置上的路径参数item_idgo,并把go的值传递给路径函数的参数go

运行示例并访问http://127.0.0.1:8000/items/para1/para2可获得如下相应:

{"item_id":"para2"}

声明路径参数的类型

使用Python标准类型注释,声明路径操作函数中路径参数的类型。

from fastapi import FastAPIapp = FastAPI()@app.get("/items/{item_id}")
async def read_item(item_id: int):return {"item_id": item_id}

上例把item_id的类型声明为int

检查:类型声明将为函数提供错误检查、代码补全等编辑器支持

数据转换

运行上述声明item_id的类型为int的例子并访问 http://127.0.0.1:8000/items/3,返回的相应如下:

{"item_id":3}

可见,函数接收并返回的值是3int),不是"3"str)。FastAPI通过类型声明自动解析请求中的数据

数据校验

如果通过浏览器访问http://127.0.0.1:8000/items/foo,将会接收如下HTTP错误信息:

{"detail": [{"type": "int_parsing","loc": ["path","item_id"],"msg": "Input should be a valid integer, unable to parse string as an integer","input": "foo"}]
}

因为路径参数item_id的值("foo")的类型不是int

检查:FastAPI使用Python类型声明实现了数据校验,上面的错误清晰的指出了未通过校验的具体原因,这在开发调试与API交互的代码时非常有用。

查看文档

访问 http://127.0.0.1:8000/docs,查看自动生成的 API 文档:

Pydantic

FastAPI可以充分利用Pydanic的优势,用它在后台校验数据。

路径操作的顺序

from fastapi import FastAPIapp = FastAPI()@app.get("/users/me")
async def read_user_me():return {"user_id": "the current user"}@app.get("/users/{user_id}")
async def read_user(user_id: str):return {"user_id": user_id}

/users/me/users/{user_id}不能反过来,否则/users/me的路径会被/users/{user_id}接收。

预设值

路径操作使用Python的Enum类型接收预设的路径参数。

from enum import Enumfrom fastapi import FastAPIclass ModelName(str, Enum):alexnet = "alexnet"resnet = "resnet"lenet = "lenet"app = FastAPI()@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):if model_name is ModelName.alexnet:return {"model_name": model_name, "message": "Deep Learning FTW!"}if model_name.value == "lenet":return {"model_name": model_name, "message": "LeCNN all the images"}return {"model_name": model_name, "message": "Have some residuals"}
  • 导入Enum并创建继承自strEnum的子类。通过从str继承,API文档就能把值的类型定义为字符串,并且能正确渲染。然后创建包含固定值的类属性,这些固定值是可用的有效值。
  • 使用Enum类(ModelName)创建使用类型注解的路径参数。
  • API文档会显示预定义路径参数的可用值。
    在这里插入图片描述
  • 使用Python枚举类型:路径参数的值是枚举的元素。枚举类ModelName中的枚举元素支持比较操作(if model_name is ModelName.alexnet),使用model_name.value获取枚举值。
  • 返回枚举元素:即使嵌套在JSON请求体里(例如,dict),也可以从路径操作返回枚举元素。

包含路径的路径参数

from fastapi import FastAPIapp = FastAPI()@app.get("/files/{file_path:path}")
async def read_file(file_path: str):return {"file_path": file_path}

参数名为file_path,结尾部分的:path说明该参数应匹配路径。

查询参数

声明的参数不是路径参数时,路径操作函数会把该参数自动解释为查询参数

from fastapi import FastAPIapp = FastAPI()fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):return fake_items_db[skip : skip + limit]

查询字符串是键值对的集合,这些键值对位于URL的?之后,以&分隔。
例如,以下URL中:

http://127.0.0.1:8000/items/?skip=0&limit=2

查询参数为:skip: 0limit: 2
这些值都是URL的组成部分,因此它们的类型本应是字符串。
但声明Python类型(上例中为int)之后,这些值就会转换为声明的类型,并进行类型校验。
所有应用于路径参数的流程也适用于查询参数。

默认值

查询参数由默认值,如上例。
可以进行如下URL访问:

  • http://127.0.0.1:8000/items/
  • http://127.0.0.1:8000/items/?skip=0&limit=10
  • http://127.0.0.1:8000/items/?skip=20

可选参数

把默认值设为None即可声明可选的查询参数:

from fastapi import FastAPIapp = FastAPI()@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None):if q:return {"item_id": item_id, "q": q}return {"item_id": item_id}

检查:注意,FastAPI 可以识别出 item_id 是路径参数,q 不是路径参数,而是查询参数。

查询参数类型转换

FastAPI会自动转换参数类型:

from typing import Unionfrom fastapi import FastAPIapp = FastAPI()@app.get("/items/{item_id}")
async def read_item(item_id: str, q: Union[str, None] = None, short: bool = False):item = {"item_id": item_id}if q:item.update({"q": q})if not short:item.update({"description": "This is an amazing item that has a long description"})return item

在本例中访问:

http://127.0.0.1:8000/items/foo?short=1

http://127.0.0.1:8000/items/foo?short=True

或short=on,short=true,short=yes或其它任意大小写形式,函数接收到的short参数都是布尔值TrueFalse同理(0,false,off,no)。

多个路径和查询参数

FastAPI可以识别同时声明的多个路径参数和查询参数,而且声明查询参数的顺序并不重要,FastAPI通过参数名进行检测:

from typing import Unionfrom fastapi import FastAPIapp = FastAPI()@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(user_id: int, item_id: str, q: Union[str, None] = None, short: bool = False
):item = {"item_id": item_id, "owner_id": user_id}if q:item.update({"q": q})if not short:item.update({"description": "This is an amazing item that has a long description"})return item

把不是路径参数的参数(至此只有查询参数)声明为默认值,或者是吧默认值设为None,这样参数就不是必选的,否则是必选的。

上述代码可以用以下URL进行测试:

http://127.0.0.1:8000/users/2/items/item_id?q=test&short=0

将会得到这样的相应:

{"item_id":"item_id","owner_id":2,"q":"test","description":"This is an amazing item that has a long description"}

再比如:

from typing import Unionfrom fastapi import FastAPIapp = FastAPI()@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str, skip: int = 0, limit: Union[int, None] = None
):item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}return item

上例中有3个查询参数:needy,必选的str类型参数,skip,默认值为0int类型参数,limit,可选的int类型参数。

请求体

FastAPI使用请求体从客户端(例如浏览器)向API发送数据。

请求体是客户端发送给API的数据。响应体是API发送给客户端的数据。

API基本上肯定要发送响应体,但是客户端不一定发送请求体

使用Pydantic模型声明请求体,能充分利用它的功能和优点。

使用Pydantic声明请求体

from fastapi import FastAPI
from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneapp = FastAPI()@app.post("/items/")
async def create_item(item: Item):return item

步骤如下:

  • pydantic中导入BaseModel
  • 创建数据类型:把数据模型声明为继承BaseModel的类。使用Python标准类型声明所有属性。
  • 声明请求体参数:使用与声明路径和查询参数相同的方式声明请求体,把请求体添加至路径操作,@app.post()及其下面的函数async def create_item(item: Item):...,此处请求体参数的类型为Item类型。

使用如下代码进行测试:

import requests# 定义请求的 JSON 数据
item_data = {"name": "Item1","description": "This is item 1","price": 19.99,"tax": 2.00
}# 发送 POST 请求
response = requests.post("http://localhost:8000/items/", json=item_data)# 打印返回的 JSON 数据
print(response.json())

可以看到,FastAPI接收来自测试代码的请求,返回:

{'name': 'Item1', 'description': 'This is item 1', 'price': 19.99, 'tax': 2.0}

结论

仅使用Python类型声明,FastAPI就可以

  • 以JSON形式读取请求体
  • (在必要时)把请求体转换为对应的类型
  • 校验数据:数据无效时返回错误信息,并指出错误数据的确切位置和内容
  • 把接收的数据赋值给参数item
  • 为模型生成JSON Schema,在项目中所需的位置使用

使用模型

在路径操作函数内部直接访问模型对象的属性:

from fastapi import FastAPI
from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneapp = FastAPI()@app.post("/items/")
async def create_item(item: Item):item_dict = item.dict()if item.tax:price_with_tax = item.price + item.taxitem_dict.update({"price_with_tax": price_with_tax})return item_dict

用测试代码测试结果如下:

{'description': 'Mechanical keyboard','name': 'Keyboard','price': 49.99,'price_with_tax': 54.99,'tax': 5.0}

请求体+路径参数

FastAPI支持同时声明路径参数和请求体。

FastAPI能识别与路径参数匹配的函数参数,还能识别从请求体中获取的类型为Pydantic模型的函数参数。

from fastapi import FastAPI
from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneapp = FastAPI()@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):return {"item_id": item_id, **item.dict()}

使用如下测试代码发送PUT请求:

import requests
from pprint import pprint# 定义要发送的 JSON 数据,符合 Item 模型的定义
item_data = {"name": "Mouse","description": "Wireless mouse","price": 19.99,"tax": 1.50
}# 定义要更新的 item_id
item_id = 1# 发送 PUT 请求
response = requests.put(f"http://localhost:8000/items/{item_id}", json=item_data)# 打印返回的 JSON 数据
pprint(response.json())

返回结果为:

{'description': 'Wireless mouse','item_id': 1,'name': 'Mouse','price': 19.99,'tax': 1.5}

请求体+路径参数+查询参数

FastAPI支持同时声明请求体路径参数查询参数

from fastapi import FastAPI
from pydantic import BaseModelclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneapp = FastAPI()@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):result = {"item_id": item_id, **item.dict()}if q:result.update({"q": q})return result

函数参数按如下规则进行识别:

  • 路径中声明了相同参数的参数,是路径参数
  • 类型是(int,float,str,bool等)单类型的参数,是查询参数
  • 类型是Pydantic模型的参数,是请求体

使用如下测试代码:

import requests
from pprint import pprint# 定义要发送的 JSON 数据,符合 Item 模型的定义
item_data = {"name": "Keyboard","description": "Mechanical keyboard","price": 49.99,"tax": 5.00
}# 定义要更新的 item_id
item_id = 1# 定义查询参数 q
q_param = "example"# 发送 PUT 请求
response = requests.put(f"http://localhost:8000/items/{item_id}?q={q_param}", json=item_data)# 打印返回的 JSON 数据
pprint(response.json())

可得到返回结果:

{'description': 'Mechanical keyboard','item_id': 1,'name': 'Keyboard','price': 49.99,'q': 'example','tax': 5.0}

查询参数和字符串校验

FastAPI允许你为参数声明额外的信息和校验。
以下面的应用程序为例:

from fastapi import FastAPIapp = FastAPI()@app.get("/items/")
async def read_items(q: str | None = None):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

查询参数q的类型为str,默认值为None,因此它是可选的。

额外的校验

我们打算添加约束条件:即使q是可选的,但只要提供了该参数,则该参数值不能超过50个字符的长度

具体步骤如下:

  • 导入Query:首先从fastapi导入Query
  • 使用Query作为默认值:将Query用作查询参数的默认值,并将它的max_length参数设置为50,由于我们必须用Query(default=None)替换默认值NoneQuery的第一个参数同样是用于定义默认值。
from typing import Unionfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, max_length=50)):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

添加更多的校验

  • 还可以添加min_length参数
  • 还可以添加正则表达式:定义一个参数值必须匹配的正则表达式
from typing import Unionfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, min_length=3, max_length=50, pattern="^fixedquery$"),
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

声明为必需参数

当我们不需要声明额外的校验或元数据时,只需不声明默认值就可以使q参数成为必需参数,如:

q: str

但是我们现在正在用Query声明它,如:

q: Union[str, None] = Query(default=None, min_length=3)

因此,当你在使用Query且需要声明一个值是必需的时,只需不声明默认参数:

from fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: str = Query(min_length=3)):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

显式声明必需参数的方法

  • 使用(...)声明:q: str = Query(default=..., min_length=3)
  • 可以声明None为一个有效的类型,仍是必需参数:q: Union[str, None] = Query(default=..., min_length=3)
  • 使用Pydantic中的Required代替省略号...from pydantic import Requiredq: str = Query(default=Required, min_length=3)

大多数情况,隐式省略default参数就够了,通常不必使用显式声明...Required

查询参数列表/多个值

当你使用Query显式地定义查询参数时,你还可以声明它去接收一组值,或换句话来说,接收多个值。

例如,要声明一个可在URL中出现多次的查询参数q,可以这样写:

from typing import List, Unionfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: Union[List[str], None] = Query(default=None)):query_items = {"q": q}return query_items

然后输入以下网址:

http://localhost:8000/items/?q=foo&q=bar

你会在路径操作函数的函数参数q中以一个Pythonlist的形式接收到查询参数q的多个值:

{"q":["foo","bar"]}

要声明类型为list的查询参数,需要显式地使用Query,否则该参数将被解释为请求体

具有默认值的查询参数列表/多个值

可以给定默认list值:

from typing import Listfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: List[str] = Query(default=["foo", "bar"])):query_items = {"q": q}return query_items

q: List[str] = Query(default=["foo", "bar"])也可以使用list代替,这样不会检查列表的内容,如q: list = Query(default=[])

声明更多元数据

你可以添加更多有关该参数的信息。
这些信息将包含在生成的OpenAPI模式中,并由文档用户界面和外部工具所使用。

  • 添加title
from typing import Unionfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, title="Query string", min_length=3),
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results
  • 添加description
from typing import Unionfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None,title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,),
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

别名参数(alias)

假设你想要查询参数为item-query,如下:

http://127.0.0.1:8000/items/?item-query=foobaritems

但是item-query不是一个有效的Python变量名称,这时可以用alias参数声明一个别名,该别名将用于在URL中查找查询参数值:

from typing import Unionfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None, alias="item-query")):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

弃用参数(deprecated)

假设你不再喜欢该参数。

你不得不将其保留一段时间,因为有些客户端正在使用它,但你希望文档清楚地将其展示为已弃用

那么将deprecated=True传入Query

from typing import Unionfrom fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
async def read_items(q: Union[str, None] = Query(default=None,alias="item-query",title="Query string",description="Query string for the items to search in the database that have a good match",min_length=3,max_length=50,pattern="^fixedquery$",deprecated=True,),
):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results

文档会像下面这样展示它:
在这里插入图片描述

总结

你可以为查询参数声明额外的校验和元数据。
通用的校验和元数据:

  • alias
  • title
  • description
  • deprecated

特定于字符串的校验:

  • min_length
  • max_length
  • regex

路径参数和数值校验

与使用Query为查询参数声明更多的校验和元数据的方式相同,你也可以使用Path为路径参数声明相同类型的校验和元数据。

具体步骤如下:

  • 导入Path:首先从fastapi导入Path
  • 声明元数据:可以声明与Query相同的所有参数,例如,要声明路径参数item_idtitle元数据值,可以输入item_id: Annotated[int, Path(title="The ID of the item to get")]
from typing import Annotatedfrom fastapi import FastAPI, Path, Queryapp = FastAPI()@app.get("/items/{item_id}")
async def read_items(item_id: Annotated[int, Path(title="The ID of the item to get")],q: Annotated[str | None, Query(alias="item-query")] = None,
):results = {"item_id": item_id}if q:results.update({"q": q})return results

路径参数总是必需的

按需对参数排序

假设你想要声明一个必需的str类型查询参数q
而且你不需要为该参数声明任何其他内容,所以实际上并不需要使用Query
但是你仍然需要使用Path来声明路径参数item_id
如果你将带有【默认值】的参数放在没有【默认值】的参数之前,Python将会报错。
但是你可以对其重新排序,并将不带默认值的值(查询参数q)放到最前面。
对FastAPI来说这无关紧要。它将通过参数的名称、类型和默认值声明(QueryPath等)来检测参数,而不在乎参数的顺序。

因此可以将函数声明为:

from fastapi import FastAPI, Pathapp = FastAPI()@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):results = {"item_id": item_id}if q:results.update({"q": q})return results

还可以用*表示后面的所有参数作为关键字参数,也被称为kwargs来调用,即使它们没有默认值

from fastapi import FastAPI, Pathapp = FastAPI()@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):results = {"item_id": item_id}if q:results.update({"q": q})return results

数值校验

使用QueryPath(以及后面的其他类)可以声明字符串约束,也可以声明数值约束。如下,添加ge=1后,item_id将必须是一个大于(greater than)或等于(equal1的整数。

  • 大于等于
from fastapi import FastAPI, Pathapp = FastAPI()@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get", ge=1), q: str
):results = {"item_id": item_id}if q:results.update({"q": q})return results
  • 大于:gt(greater than)
  • 小于等于:le(less than or equal)
from fastapi import FastAPI, Pathapp = FastAPI()@app.get("/items/{item_id}")
async def read_items(*,item_id: int = Path(title="The ID of the item to get", gt=0, le=1000),q: str,
):results = {"item_id": item_id}if q:results.update({"q": q})return results
  • 浮点数:数值校验同样适用于float值。

请求体-多个参数

既然我们已经知道了如何使用PathQuery,下面让我们来了解一下请求体声明的更高级用法。

混合使用PathQuery和请求体参数

你可以随意混合使用PathQuery和请求体参数声明。
你还可以通过将默认值设置为None来将请求体参数声明为可选参数:

from typing import Annotatedfrom fastapi import FastAPI, Path
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = None@app.put("/items/{item_id}")
async def update_item(item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],q: str | None = None,item: Item | None = None,
):results = {"item_id": item_id}if q:results.update({"q": q})if item:results.update({"item": item})return results

多个请求体参数

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneclass User(BaseModel):username: strfull_name: str | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):results = {"item_id": item_id, "item": item, "user": user}return results

在上面的情况下,FastAPI将注意到该函数中有多个请求体参数(两个Pydantic模型参数)。因此,它将使用参数名称作为请求体中的键(字段名称),并期望一个类似于以下内容的请求体:

{"item": {"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2},"user": {"username": "dave","full_name": "Dave Grohl"}
}

FastAPI将自动对请求中的数据进行转换,因此item参数将接收指定的内容,user参数也是如此。它将执行对复合数据的校验,并且像现在这样为OpenAPI模式和自动化文档对其进行记录。

请求体中的单一值(Body)

与使用QueryPath为查询参数和路径参数定义额外数据的方式相同,FastAPI提供了一个同等的Body,例如上面的模型,除了itemuser之外,还想在同一请求体中具有另一个键importance,如果按原样来声明它,因为它是一个单一值,FastAPI将假定它是一个查询参数。但是你可以使用Body指示FastAPI将其作为请求体的另一个键进行处理。

from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneclass User(BaseModel):username: strfull_name: str | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):results = {"item_id": item_id, "item": item, "user": user, "importance": importance}return results

在这种情况下,FastAPI将期望这样的请求体:

{"item": {"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2},"user": {"username": "dave","full_name": "Dave Grohl"},"importance": 5
}

多个请求体参数和查询参数

除了请求体参数外,你还可以在任何需要的时候声明额外的查询参数。
由于默认情况下单一值被解释为查询参数,因此你不必显式地添加Query

from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Noneclass User(BaseModel):username: strfull_name: str | None = None@app.put("/items/{item_id}")
async def update_item(*,item_id: int,item: Item,user: User,importance: Annotated[int, Body(gt=0)],q: str | None = None,
):results = {"item_id": item_id, "item": item, "user": user, "importance": importance}if q:results.update({"q": q})return results

嵌入单个请求体参数(Body(embed=True))

假设你只有一个来自Pydantic模型Item的请求体参数item
默认情况下,FastAPI将直接期望这样的请求体。
但是,如果你希望它期望一个拥有item键并在值中包含模型内容的JSON,就像在声明额外的请求体参数时所做的那样,则可以使用一个特殊的Body参数embed

item: Item = Body(embed=True)
from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):results = {"item_id": item_id, "item": item}return results

在这种情况下,FastAPI将期望像这样的请求体:

{"item": {"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2}
}

而不是:

{"name": "Foo","description": "The pretender","price": 42.0,"tax": 3.2
}

总结

你可以添加多个请求体参数到路径操作函数中,即使一个请求只能有一个请求体。

但是 FastAPI 会处理它,在函数中为你提供正确的数据,并在路径操作中校验并记录正确的模式。

你还可以声明将作为请求体的一部分所接收的单一值。

你还可以指示 FastAPI 在仅声明了一个请求体参数的情况下,将原本的请求体嵌入到一个键中。

请求体-字段(Field)

与在路径操作函数中使用QueryPathBody声明校验与元数据的方式一样,可以使用Pydantic的Field在Pydantic模型内部声明校验和元数据。

具体步骤如下:

  • 导入Field:首先从Pydantic中导入Field
  • 声明模型属性:使用Field定义模型的属性。
from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModel, Fieldapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Field(default=None, title="The description of the item", max_length=300)price: float = Field(gt=0, description="The price must be greater than zero")tax: float | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):results = {"item_id": item_id, "item": item}return results

Field的工作方式和QueryPathBody相同,参数也相同。

请求体-嵌套类型

使用FastAPI,你可以定义、校验、记录文档并使用任意深度嵌套的模型(归功于Pydantic)。

List字段

你可以将一个属性定义为拥有子元素的类型。例如Pythonlist

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: 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成为一个由元素组成的列表。不过它没有声明每个元素的类型。

具有子类型的List字段

但是Python有一种特定的方法来声明具有子类型的列表:

具体步骤如下:

  • 声明具有子类型的List:从typing模块导入它们,使用方括号[]将子类型作为【类型参数】传入。
  • 在我们的示例中,我们可以将tags明确地指定为一个【字符串列表】。
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: 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

Set类型

标签不应该重复,所以用Settag声明为一个由str组成的set

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: 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模型
  • Image模型用作一个属性的类型
from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Image(BaseModel):url: strname: strclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: 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

这意味着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"}
}

特殊的类型和校验

除了普通的单一值类型(如strintfloat等)外,你还可以使用从str继承的更复杂的单一值类型。

要了解所有的可用选项,查看来自Pydantic的外部类型的文档。

例如,在Image模型中我们有一个url字段,我们可以把它声明为Pydantic的HttpUrl,而不是str

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Image(BaseModel):url: HttpUrlname: strclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: 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

该字符串将被检查是否为有效的URL,并在JSON Schema/OpenAPI文档中进行记录。

带有一组子模型的属性

你还可以将Pydantic模型用作listset等的子模型:

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Image(BaseModel):url: HttpUrlname: strclass Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: 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

这将期望(转换,校验,记录文档等)下面这样的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"}]
}

深度嵌套模型

你可以定义任意深度的嵌套模型:

from typing import Unionfrom fastapi import FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Image(BaseModel):url: HttpUrlname: strclass Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: Union[float, None] = Nonetags: set[str] = set()images: Union[list[Image], None] = Noneclass Offer(BaseModel):name: strdescription: Union[str, None] = Noneprice: floatitems: list[Item]@app.post("/offers/")
async def create_offer(offer: Offer):return offer

纯列表请求体

如果你期望的JSON请求体的最外层是一个JSONarray(即Python list),则可以在路径操作函数的参数中声明此类型,就像声明Pydantic模型一样:

images: List[Image]

例如:

from fastapi import FastAPI
from pydantic import BaseModel, HttpUrlapp = FastAPI()class Image(BaseModel):url: HttpUrlname: str@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):return images

任意dict构成的请求体

你也可以将请求体声明为使用某类型的键和其他类型值的dict
无需事先知道有效的字段/属性(在使用Pydantic模型的场景)名称是什么。
如果你想接收一些尚且未知的键,这将很有用。

其他有用的场景是当你想要接收其他类型的键时,例如int,如下,你将接受任意键为int类型并且值为float类型的dict

from fastapi import FastAPIapp = FastAPI()@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):return weights

JSON仅支持将str作为键,但是Pydantic具有自动转换数据的功能。这意味着,即使你的API客户端只能将字符串作为键发送,只要这些字符串内容仅包含整数,Pydantic就会对其进行转换并校验。然后你接收的名为weightsdict实际上将具有int类型的键和float类型的值。

模式的额外信息-例子

你可以在JSON模式中定义额外的信息。
一个常见的用例是添加一个将在文档中显示的example
有几种方法可以声明额外的JSON模式信息。

Pydantic schema_extra

你可以使用Configschema_extra为Pydantic模型声明一个示例,如Pydantic文档:定制Schema中所述。

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonemodel_config = {"json_schema_extra": {"examples": [{"name": "Foo","description": "A very nice Item","price": 35.4,"tax": 3.2,}]}}@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):results = {"item_id": item_id, "item": item}return results

这些额外的信息将按原样添加到输出的JSON模式中。

Field的附加参数

FieldPathQueryBody和其他你之后将会看到的工厂函数,你可以为JSON模式声明额外信息,你也可以通过给工厂函数传递其他的任意参数来给JSON模式声明额外信息,比如增加example

from fastapi import FastAPI
from pydantic import BaseModel, Fieldapp = FastAPI()class Item(BaseModel):name: str = Field(examples=["Foo"])description: str | None = Field(default=None, examples=["A very nice Item"])price: float = Field(examples=[35.4])tax: float | None = Field(default=None, examples=[3.2])@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):results = {"item_id": item_id, "item": item}return results

传递的那些额外参数不会添加任何验证,只会添加注释,用于文档的目的。

Body额外参数

from typing import Annotatedfrom fastapi import Body, FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = None@app.put("/items/{item_id}")
async def update_item(item_id: int,item: Annotated[Item,Body(examples=[{"name": "Foo","description": "A very nice Item","price": 35.4,"tax": 3.2,}],),],
):results = {"item_id": item_id, "item": item}return results

额外数据类型

到目前为止,一直在使用常见的数据类型,如:

  • int
  • float
  • str
  • bool
    但是也可以使用更复杂的数据类型。

其他数据类型

  • UUID:
    一种标准的 “通用唯一标识符” ,在许多数据库和系统中用作ID。
    在请求和响应中将以 str 表示。
  • datetime.datetime:
    一个 Python datetime.datetime.
    在请求和响应中将表示为 ISO 8601 格式的 str ,比如: 2008-09-15T15:53:00+05:00.
  • datetime.date:
    Python datetime.date.
    在请求和响应中将表示为 ISO 8601 格式的 str ,比如: 2008-09-15.
  • datetime.time:
    一个 Python datetime.time.
    在请求和响应中将表示为 ISO 8601 格式的 str ,比如: 14:23:55.003.
  • datetime.timedelta:
    一个 Python datetime.timedelta.
    在请求和响应中将表示为 float 代表总秒数。
    Pydantic 也允许将其表示为 “ISO 8601 时间差异编码”, 查看文档了解更多信息。
  • frozenset:
    在请求和响应中,作为 set 对待:
    在请求中,列表将被读取,消除重复,并将其转换为一个 set
    在响应中 set 将被转换为 list
    产生的模式将指定那些 set 的值是唯一的 (使用 JSON 模式的 uniqueItems)。
  • bytes:
    标准的 Python bytes
    在请求和响应中被当作 str 处理。
    生成的模式将指定这个 strbinary “格式”。
  • Decimal:
    标准的 Python Decimal
    在请求和响应中被当做 float 一样处理。
    您可以在这里检查所有有效的pydantic数据类型: Pydantic data types.

例子

from datetime import datetime, time, timedelta
from typing import Annotated
from uuid import UUIDfrom fastapi import Body, FastAPIapp = FastAPI()@app.put("/items/{item_id}")
async def read_items(item_id: UUID,start_datetime: Annotated[datetime, Body()],end_datetime: Annotated[datetime, Body()],process_after: Annotated[timedelta, Body()],repeat_at: Annotated[time | None, Body()] = None,
):start_process = start_datetime + process_afterduration = end_datetime - start_processreturn {"item_id": item_id,"start_datetime": start_datetime,"end_datetime": end_datetime,"process_after": process_after,"repeat_at": repeat_at,"start_process": start_process,"duration": duration,}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/37285.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

elementUI的搭建使用过程

Element - The worlds most popular Vue UI framework 上面是elementUI的网站,进入网站 点击右上角的组件按钮 复制这段代码到你的项目终端:npm i element-ui -S 加载完成后即可使用elementUI网站中的组件,使用它们只需要复制组件下面的代码即可

Unity UGUI 实现简单两点连线功能

实现 记录鼠标点击位置为线段起点。 posStart Input.mousePosition; 创建一个Image 作为线段。 line new GameObject("line"); rtLine line.AddComponent<RectTransform>(); rtLine.pivot new Vector2(0, 0.5f); rtLine.localScale Vector3.one; img…

在操作系统中,background通常指的是运行于后台的进程或任务

在计算机中&#xff0c;"background"一词具有多种含义&#xff0c;以下是一些主要的解释和相关信息&#xff1a; 计算机视觉中的背景&#xff08;Background&#xff09;&#xff1a; 在计算机视觉中&#xff0c;background指的是图像或视频中的背景部分&#xff0c;…

IOS17闪退问题Assertion failure in void _UIGraphicsBeginImageContextWithOptions

最近项目更新到最新版本IOS17&#xff0c;发现一个以前的页面突然闪退了。原来是IOS17下&#xff0c;这个方法 UIGraphicsBeginImageContext(CGSize size) 已经被移除&#xff0c;原参数如果size为0的话&#xff0c;会出现闪退现象。 根据说明&#xff0c;上述方法已经被替换…

【shell脚本速成】python安装脚本

文章目录 案例需求应用场景解决问题脚本思路案例代码 &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f388;欢迎踏入我的博客世界&#xff0c;能与您在此邂逅&#xff0c;真是缘分使然&#xff01;&#x1f60a; &#x1f338;愿您在此停留的每一刻&#xff0c;都沐…

React 中 useEffect

React 中 useEffect 是副作用函数&#xff0c;副作用函数通常是处理外围系统交互的逻辑。那么 useEffect 是怎处理的呢&#xff1f;React 组件都是纯函数&#xff0c;需要将副作用的逻辑通过副作用函数抽离出去&#xff0c;也就是副作用函数是不影响函数组件的返回值的。例如&a…

vue中如何使用echarts和echarts-gl实现三维折线图

一、vue中使用三维折线图 效果图&#xff1a; 二、使用步骤 1.引入库 安装echarts 在package.json文件中添加 "dependencies": {"echarts": "^5.1.2""echarts-gl": "^1.1.1",// "echarts-gl": "^2.0.8…

5. Spring IoCDI ★ ✔

5. Spring IoC&DI 1. IoC & DI ⼊⻔1.1 Spring 是什么&#xff1f;★ &#xff08;Spring 是包含了众多⼯具⽅法的 IoC 容器&#xff09;1.1.1 什么是容器&#xff1f;1.1.2 什么是 IoC&#xff1f;★ &#xff08;IoC: Inversion of Control (控制反转)&#xff09;总…

AGI大模型的门槛是什么?怎么学

AGI&#xff08;Artificial General Intelligence&#xff0c;通用人工智能&#xff09;大模型&#xff0c;即具备类似人类智能的、能够理解、学习、应用知识和技能并解决各种复杂问题的通用型人工智能模型&#xff0c;目前仍然是人工智能研究的前沿领域&#xff0c;具有以下门…

深度学习 --- stanford cs231学习笔记七(训练神经网络之梯度下降优化器)

5&#xff0c;梯度下降优化器 5&#xff0c;1 梯度下降在深度学习中的作用 在深度学习中&#xff0c;权重W的值是否合理是由损失函数L来判断的。L越小&#xff0c;表示W的设置越happy。L越大&#xff0c;表示W的值越unhappy。 为了让L越来越小&#xff0c;常用的方法是梯度下降…

句法分析概述

第1关&#xff1a;句法分析概述 任务描述 本关任务&#xff1a;通过对句法分析基本概念的学习&#xff0c;完成相应的选择题。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 句法分析的基础概念&#xff1b; 句法分析的数据集和评测方法。 句法分析简介…

【问chatgpt】

这采用的是什么脚手架初始化的&#xff1f;这样问询chatgpt 从文件结构来看&#xff0c;这个项目很可能是使用 Express 框架初始化的。这些文件夹和命名约定常见于 Express 项目结构&#xff0c;特别是&#xff1a; controllers: 控制器文件夹&#xff0c;通常用于处理请求逻…

同城小商城货到付款系统源码小程序

&#xff1a;便捷购物新选择 &#x1f680; 一、快速便捷&#xff0c;同城直达 在这个快节奏的时代&#xff0c;时间就是金钱。你是否曾因为等待快递而焦虑不安&#xff1f;现在&#xff0c;有了“同城商城货到付款小程序”&#xff0c;这一切都变得不再是问题。它专注于同城…

谷歌如何进行失效链接建设?

失效链接建设是一种高效的外链建设策略&#xff0c;通过发现并利用失效链接来提升自己网站的SEO。以下是详细的步骤&#xff1a; 寻找失效页面&#xff1a;你需要使用SEO工具&#xff0c;如Ahrefs&#xff0c;来查找与你的网站内容相关的失效页面。这些页面可能是竞争对手的失…

传神社区|数据集合集第4期|中文NLP数据集合集

自从ChatGPT等大型语言模型&#xff08;Large Language Model, LLM&#xff09;出现以来&#xff0c;其类通用人工智能&#xff08;AGI&#xff09;能力引发了自然语言处理&#xff08;NLP&#xff09;领域的新一轮研究和应用浪潮。尤其是ChatGLM、LLaMA等普通开发者都能运行的…

一文弄懂线性回归模型

1、引言 今天&#xff0c;我们将深入探讨机器学习中的三个关键概念&#xff1a;线性回归、代价函数和梯度下降。这些概念构成了许多机器学习算法的基础。起初&#xff0c;我决定不写一篇关于这些主题的文章&#xff0c;因为它们已经被广泛涉及。不过&#xff0c;我改变了主意&…

[图解]SysML和EA建模住宅安全系统-02-现有运营领域-块定义图

1 00:00:00,840 --> 00:00:02,440 首先我们来看画在哪里 2 00:00:02,570 --> 00:00:08,310 你看&#xff0c;这是图的类型&#xff0c;图里面内容 3 00:00:08,320 --> 00:00:10,780 这是元素类型 4 00:00:10,790 --> 00:00:14,900 这是位置&#xff0c;哪个包 …

Halcon 文本文件操作,形态学

一文件的读写 *******************************************************向文本文件写入字符串内容*************************************************************read_image (Image, fabrik)threshold (Image, Region, 0, 120)area_center (Region, Area, Row, Column)open_…

嘉立创学习

1.两个设置&#xff0c;一般用左边那个 2.焊盘分类 基本焊盘 热风盘&#xff1a;也叫花焊盘&#xff08;负片&#xff09; 隔离焊盘&#xff1a;外面那圈黑色&#xff0c;用作隔离&#xff08;负片&#xff09; 钢网层&#xff1a;&#xff08;锡膏&#xff09; 阻焊层&…

【php】【mysql】【layui】 原生初级简易留言簿系统成品代码动态网站开发网页WEB浏览器端B/S结构

更多项目点击&#x1f446;&#x1f446;&#x1f446;完整项目成品专栏 【php】【mysql】【layui】 原生初级简易留言簿系统成品代码动态网站开发网页WEB浏览器端B/S结构 获取源码方式项目说明&#xff1a;文件包含&#xff1a;项目运行环境项目运行截图 获取源码方式 加Q群…