超全面整理fastAPI(从入门到运用,进来看十秒钟再走不迟

Written by

in

文章目录
  • 与上一节使用 Query 声明查询参数的更多验证和元数据的方法相同,也可以通过Path声明路径参数的相同类型的验证和元数据。
  • 我们可以使用Schema模型,在定义请求体模型的类属性的时候,给类属性添加默认值或者验证
  • ==我们可以将请求体的属性值类型设置为列表、元组,列表里面的元素可以是正常的数据类型,也可以是请求体模型。当然,请求体的属性值也可以设置为请求体模型。==这种现象我们称之为模型的嵌套,下面我们逐一来看这些情况。
  • 除了下面常见的几种数据类型之外, int float str bool fastAPI还提供了其它更复杂的数据类型
  • 定义Cookie参数,就和你定义Query和Path参数一样的方式 第一步、导入 Cookie: from fastapi import Cookie, FastAPI 第二步、声明Cookie参数 第一个值是默认值,您可以传递所有其他验证或注释参数: ads_id: str = Cookie(None) from fastapi import Cookie, FastAPIapp = FastAPI()@app.get("/items/") async def read_items(*, ads_id: str = Cookie(None) ):return {"ads_id": ads_id} 注意两点: Cookie是Path和Query的姐妹类,它也同样继承自相同的Param类,所以适用路径参数和查询参数的验证,Cookie参数也适用 申明cookies,你需要使用Cookie方法,否则参数会被解释为查询参数
  • 定义Header参数,就和你定义Query和Path参数一样的方式 第一步、导入 Header: from fastapi import Cookie, FastAPI , Header 第二步、声明Header参数 第一个值是默认值,您可以传递所有其他验证或注释参数: ads_id: str = Header(None) from fastapi import FastAPI, Headerapp = FastAPI()@app.get("/items/") async def read_items(*, user_agent: str = Header(None)):return {"User-Agent": user_agent} 注意两点: Header是Path、Query和Cookie的姐妹类,它也同样继承自相同的Param类.,所以适用路径参数和查询参数的验证,Cookie参数也适用 申明Header,你需要使用Header方法,否则参数会被解释为查询参数
  • 可以在任何路径操作中使用参数 response_model 声明用于响应的模型: @app.get() @app.post() @app.put() @app.delete() etc.
  • 【写在前面】:大家好,我是【猪葛】
    一个很看好AI前景的算法工程师
    我自己会给公司开发很多人工智能的算法功能,但是因为后端人员很忙,
    所以我自己得把功能封装成接口部署到服务器,
    所以自己就自学FastAPI框架
    这个框架非常适合新手入门后端的接口开发
    我自己也就学了不到一个星期,现在将学习过程的笔记分享出来
    希望能给大家一点帮助,如果你也喜欢我的博客,欢迎关注我的动态,一起学习一起进步
    入门从第一章到第四章即可
    提升从第五章到第七章
    进阶从第八章往后,以后会进一步更新更高级用法

    本篇博客学习目标:1、学会用fastAPI框架搭建最简单的服务器

    • 一、fastapi的安装
      • 1-1、使用pip安装
      • 1-2、验证是否安装成功
      • 1-3、了解FastAPI程序结构
    • 二、路径操作装饰器中的路径参数
      • 2-1、声明路径参数
      • 2-2、声明路径参数的类型
      • 2-3、限定路径参数有效值
      • 2-4、路径参数的值是路径类型变量
    • 三、查询参数
      • 3-1、查询参数概念
      • 3-2、给查询参数设置默认值
      • 3-3、设置可选的查询参数
    • 四、请求体
      • 4-1、什么是请求体(Request Body)
      • 4-2、如何实现请求体
      • 4-3、ubuntu18安装postman
      • 4-4、使用请求体模型
    • 五、给查询参数设置验证条件(字符串验证)
      • 5-1、查询参数简单回顾
      • 5-2、为查询参数添加验证
      • 5-3、声明在URL中出现多次的查询参数
    • 六、给路径参数设置验证条件(数值验证)
      • 6-1、给路径参数添加验证
      • 6-2、参数排序技巧
      • 6-3、给路径参数添加数字验证:对数字大小
    • 七、请求体多种参数
      • 7-1、混合使用`Path`, `Query` 和 请求体参数
      • 7-2、多个模型的请求体参数
      • 7-3、使用Body方法定义单值的请求体参数
      • 7-4、嵌入单个请求体参数
    • 八、Body-Schema模型
      • 8-1、创建Schema模型
      • 8-2、额外参数给请求体添加示例字段
    • 九、Body – Nested Models(嵌套模型)
      • 9-1、请求体模型的属性值类型为列表
      • 9-2、深层嵌套模型
    • 十、Extra data types(额外数据类型)
      • 10-1、其他数据类型
      • 10-2、举例说明
    • 十一、Cookie Parameters(Cookie参数)
    • 十二、Header Parameters(Header参数)
    • 十三、Response Model(响应模型)
      • 13-1、响应模型的定义
      • 13-2、响应模型的使用

    安装fastapi的语句

    pip install fastapi
    

    当然你可以使用国内阿里云镜像源进行安装,会快很多,上面的语句变成下面的:

    pip install fastapi -i https://mirrors.aliyun.com/pypi/simple
    

    因为fastapi启动依赖于uvicorn,所以我们还需要安装uvicorn

    pip install uvicorn -i https://mirrors.aliyun.com/pypi/simple
    

    到这里,fastapi就安装完毕了,下面我们来验证一下安装是否成功

    新建名字叫main.py的文件,将下面内容复制到里面去

    from fastapi import FastAPIapp = FastAPI()@app.get("/")
    async def root():return {"message": "Hello World"}
    

    然后使用终端开启uvicorn服务

    uvicorn main:app --reload
    

    uvicorn main:app 命令指:

    • main: main.py 文件(也可理解为Python模块).
    • app: main.py 中app = FastAPI()语句创建的app对象.
    • --reload: 在代码改变后重启服务器,只能在开发的时候使用

    你将会看到如下的输出:

    INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
    INFO:     Started reloader process [8438] using statreload
    INFO:     Started server process [8440]
    INFO:     Waiting for application startup.
    INFO:     Application startup complete.

    然后打开浏览器,输入: http://127.0.0.1:8000。看看有没有打印出:"message": "Hello World" 这句话,如果有,表示安装成功。

    如果你此时访问 http://127.0.0.1:8000/docs。你将会看到自动生成的API交互文档。这点很重要,也是fastapi的一个优点。

    编写一个简单的FastAPI程序需要五个小步骤,先看一个完整例子

    from fastapi import FastAPIapp = FastAPI()@app.get("/")
    def root():return {"message": "Hello World"}
    

    第一步,导入FastAPI

    from fastapi import FastAPI
    

    第二步,创建一个app实例

    app = FastAPI()
    

    第三步,编写一个 路径操作装饰器

    @app.get("/")
    

    需要注意的两点是:

    • 你可以将get操作方法更改成@app.post()@app.put()@app.delete()等方法

    • 你可以更改相应的路径("/")为自己想要的,例如我更改为("/hello_word/")

    第四步,编写一个路径操作函数,例如下面代码中的root函数。它位于路径操作装饰器下方(见上方例子)

    def root():return {"message": "Hello World"}
    

    这个函数的返回值可以是

    dictlist,单独的值,比如strint,或者是Pydantic模型

    第五步、运行开发服务器uvicorn main:app --reload即可访问api链接。

    例如我在终端运行uvicorn main:app --reload之后,在浏览器输入127.0.0.1:8000,出现"message": "Hello World"这句话。

    在这里可以自己指定要运行的服务器ip和端口号。

    例如:uvicorn main:app --host 127.0.0.1 --port 8001 --reload表示指定本地电脑为服务器,端口号为8001。下面所有的代码演示都默认这个本机ip地址和8001端口号

    本节总结:虽然这个例子很简单,但是却把编写fastapi的流程和结构全都包含在里面了,以后功能的细化无非就是细化第三和第四部分而已。

    使用Python格式字符串的语法声明路径参数,例子

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

    上述代码运行之后,路径参数 item_id 的值会作为read_item函数参数 item_id 的值。

    因此,如果你运行上述示例,然后跳转到 http://127.0.0.1:8000/items/foo, 你将会看见这样的回应:

    {"item_id":"foo"}
    

    使用 标准的Python类型注释在函数中声明路径参数的类型,例子1:

    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类型

    例子2:

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

    上述将参数item_name的类型定义为str类型

    当我们声明了路径参数的类型,如果我们在访问链接的时候提供的参数类型不对,FastAPI还会自动为我们做数据校验的功能,在开发和调试与您的API交互的代码时,这非常有用。注意两点:

    • 所有的数据验证都是由 Pydantic实现的.
    • 你可以用同样的类型声明比如 str, float, bool 或者其他更复杂的类型.

    有时候我们只想给某个路径参数传递某几个固定的有效值,我们就可以使用到这个方法。先看完整例子代码

    from enum import Enum
    from fastapi import FastAPIclass Hjx_Class_name(str, Enum):Name = 'huangjunx'Year = 18Id = '20153201072'student = Trueapp = FastAPI()@app.get('/hjx/{hjx_man}')
    def root(hjx_man: Hjx_Class_name):return {'status': hjx_man}

    第一步、创建一个继承strEnum的类,并创建几个类属性,这些类属性的值将是可用的有效值

    第二步、声明路径参数。路径参数hjx_man的值将传递给函数root的参数hjx_man,并且这个值的取值范围只能是Hjx_Class_name类中类属性的值。

    例如你访问http://127.0.0.1:8001/hjx/20153201072,得到的会是:{“status”:“20153201072”}

    例如你访问http://127.0.0.1:8001/hjx/True,得到的会是:{“status”:“True”}

    这样我们就能做到给某个路径参数传递某几个固定的有效值了。

    进一步,我们还可以在root函数里面调用这个类的类属性。通过Hjx_Class_name.Name进行调用。下面例子无论你使用哪个类属性的值访问,结果都是{"status":"huangjunx"}

    from enum import Enum
    from fastapi import FastAPIclass Hjx_Class_name(str, Enum):Name = 'huangjunx'Year = 18Id = '20153201072'student = Trueapp = FastAPI()@app.get('/hjx/{hjx_man}')
    def root(hjx_man: Hjx_Class_name):return {'status': Hjx_Class_name.Name}

    假设现在你有一个路径操作:/files/{file_path},但是你需要 file_path 本身包含一个 路径, 比如 home/johndoe/myfile.txt.

    因此, 文件路径可能是: /files/home/johndoe/myfile.txt

    在这种情况,我们使用Path转换器就可以进行转换了。使用以下方法声明值是路径的路径参数

    /files/{file_path:path}
    

    语句表示的意思是:参数的名字是 file_path:path说明参数file_path对应的类型是 path 类型.

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

    当你声明不属于路径参数的其他函数参数时,它们将自动解释为“Query”参数,也就是查询参数

    查询参数就是一系列在URL?之后的key-value键值对,每对键值对用 & 分割开来。例如

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

    查询参数有两个,一个是skip,一个是limit,它们的值分别为0,10

    由于它们都是URL的一部分,所以 “本质上” 它们都是字符串。

    但是当你需要使用Python类型来声明query参数的时候(例如用int),他们就会被转换为相应的类型并且依据这个类型来验证传入参数。

    适用于Path参数的所有过程也适用于Query参数

    • 编辑器支持
    • 数据解析
    • 数据验证
    • 自动文档
    from fastapi import FastAPIapp = FastAPI()@app.get("/files/")
    def add(num1: int=2, num2 int=8):return {"num1 + num2 = ": num1 + num2}
    

    当你使用浏览器访问http://127.0.0.1:8001/files/?num1=2&num2=3,你会得到:{"num1 + num2 = ":5}

    query参数类不是path中固定的一部分,所以他们是可选的,并且可以有默认值。

    例如上面的例子,当你使用浏览器访问http://127.0.0.1:8001/files/,你会得到:{"num1 + num2 = ":10}

    声明可选的Query参数,只需要将他们的默认值设置为None即可。

    关于查询参数还需要注意以下几点:

    • 如果设置的查询参数没有默认值不是None,那么这个查询参数就是必需查询参数,必须要传入,否则会报错
    • 查询参数可以和路径参数结合使用

    当您需要将数据从客户端(例如浏览器)发送到API时,可以将其作为 “请求体” 发送。

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

    API几乎总是必须发送一个响应体,但是客户端并不需要一直发送请求体。

    定义请求体,需要使用 Pydantic 模型。注意以下几点

    • 不能通过GET请求发送请求体
    • 发送请求体数据,必须使用以下几种方法之一:POST(最常见)、PUT、DELETE、PATCH

    实现请求体总共包含三个步骤。

    第一步,从pydantic中导入BaseModel

    from pydantic import BaseModel
    

    第二步,创建请求体数据模型

    声明请求体数据模型为一个类,且该类继承 BaseModel。所有的属性都用标准Python类。和查询参数一样:数据类型的属性如果不是必须的话,可以拥有一个默认值或者是可选None。否则,该属性就是必须的。

    例如,声明了一个JSON对象

    from pydantic import BaseModelclass Item(BaseModel):name: str description: str = Noneprice: float tax: float = None 
    

    所以访问链接的时候传入的请求体可以是下面两种,

    {"name": "Foo","description": "An optional description","price": 45.2,"tax": 3.5
    }
    

    另一种可以不传递默认值或者是可选值,(注意字典后面最后一个元素不需要逗号

    {"name": "Foo","price": 45.2
    }
    

    第三步、将模型定义为参数

    将上面定义的模型添加到你的路径操作中,就和定义Path和Query参数一样的方式:

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

    声明参数的类型为你创建的模型 Item

    这样当你使用postman选择post方法访问链接并传递一个值为

    {
    “name”: “Foo”,
    “price”: 45.2
    }

    的请求体之后,会得到输出

    {
    “name”: “Foo”,
    “price”: 45.2
    }

    因为后续经常会用到使用post方法访问api,为了方便测试,我们安装postman。

    安装命令

    sudo snap install postman
    

    运行完之后会看到类似下面的语句,说明安装完毕

    postman 7.35.0 from Postman, Inc. (postman-inc✓) installed

    在函数内部,可以直接访问模型对象的所有属性:

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

    注意以下几点:

    • 可以同时定义路径参数和请求体参数
    • 可以同时定义路径参数和查询参数和请求体参数,如果path中声明了某个参数,那么这个参数将作为路径参数是使用;如果参数是 单一类型(例如intfloatstrstrbool等),它将被解释为 query参数。

    我们先通过一个例子看一下查询参数是什么

    from fastapi import FastAPIapp = FastAPI()@app.get("/items/")
    def read_items(q: str = None):results = {"items": 'Big preoject'}if q:results.update({"q": q})  # 给字典results添加一个健值对{"q": q}return results

    查询 参数 qstr 类型, 并且默认为 None, 说明它是可选的

    我们将设置:即使 q 是可选的,只要提供了q,它的长度就不能超过50个字符

    第一步,导入Query

    from fastapi import FastAPI, Query
    

    第二步、使用 Query 设置max_length验证

    from fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
    def read_items(q: str = Query(None, max_length=50)):results = {"items": 'Big preoject'}if q:results.update({"q": q})  # 给字典results添加一个健值对{"q": q}return results

    说明几点:

    一、因为我们必须用 Query(None) 替换默认值None,所以Query的第一个参数是定义默认值。当默认值为None,也表示这个参数是可选的。当默认值为...,表示这个参数是必选的。

    q: str = Query(None,  max_length=50)  # 可选
    q: str = Query(... , max_length=50)  # 必选
    

    例如你将默认值设置为’hello word’

    q: str = Query('hello word',  max_length=50)
    
    from fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
    async def read_items(q: str = Query('hello world', max_length=50)):results = {"items": 'Big preoject'}if q:results.update({"q": q})  # 给字典results添加一个健值对{"q": q}return results

    二、这样我们就可以向Query对象中传递更多验证参数来验证查询参数,例如本例的max_length参数

    这将验证数据,当数据无效时显示清楚的错误,并在OpenAPI Schema路径操作中记录参数

    例如:添加 min_length参数,设置最短长度

    q: str = Query(None, min_length=n, max_length=m)
    
    from fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
    async def read_items(q: str = Query(None, max_length=50, min_length=3)):results = {"items": 'Big preoject'}if q:results.update({"q": q})  # 给字典results添加一个健值对{"q": q}return results

    如果我传递的字符串长度小于3的时候,会显示友好的报错信息: “msg”: “ensure this value has at least 3 characters”

    {"detail": [{"loc": ["query","q"],"msg": "ensure this value has at least 3 characters","type": "value_error.any_str.min_length","ctx": {"limit_value": 3}}]
    }
    

    例如:添加正则表达式验证

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

    这个特定的正则表达式检查接收到的参数值:

    • ^: 以…开头,表示字符串fixedquery前面没有字符。
    • fixedquery: 匹配 fixedquery 字符串.
    • $: 以…结尾,表示字符串fixedquery后面不匹配任何字符.

    除了验证信息,你还可以给查询参数添加更多信息,例如,你可以添加参数标题title

    from fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
    async def read_items(q: str = Query(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 fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
    async def read_items(q: str = Query(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
    

    或者你还可以给参数起一个别名,例如下面给查询参数q起个别名叫item-query

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

    或者你还可以给参数做个标记,说明这个参数已被弃用,这样你既可以给参数保留一段时间,又能在文档中清楚地表示这个参数将被放弃。

    from fastapi import FastAPI, Queryapp = FastAPI()@app.get("/items/")
    async def read_items(q: str = Query(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,regex="^fixedquery$",deprecated=True,    )
    ):results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}if q:results.update({"q": q})return results
    

    5-2小节总结:我们可以给查询参数声明一些额外的验证和元数据,常用的验证参数有aliastitledescriptiondeprecated,常见的对字符串的验证有min_lengthmax_lengthregex

    在URL中出现多次的查询参数是什么样子的呢?,例如

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

    从上面可以看到,同一个查询参数可以同时赋予不同的值

    如何定义呢?你可以这样写:

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

    如果,URL是

    127.0.0.1:8001/items/?q=q1&q=q2
    

    那么,得到的输出是

    {"q": ["q1","q2"]
    }
    

    当然,我们除了可以给它设置可选默认值,我们也可以给它设置一个默认值

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

    这样子,URL是

    127.0.0.1:8001/items/
    

    那么,输出是

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

    与上一节使用 Query 声明查询参数的更多验证和元数据的方法相同,也可以通过Path声明路径参数的相同类型的验证和元数据。

    首先,从fastapi中导入 Path :

    from fastapi import FastAPI, Path, Query 
    

    然后,使用Path声明元数据,你可以声明 Query 中所有参数。例如

    item_id: int = Path(..., title="The ID of the item to get"),    q: str = Query(None, alias="item-query")
    

    注意一点,因为Path 参数是必须的(它是路径URL的一部分),因此你需要用...声明标志这是必需参数

    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
    

    如果要声明q查询参数而不使用Query或任何默认值,并且使用Path声明路径参数item_id并使用不同的顺序,则Python对此有一些特殊的语法。语法规则如下

    • 传递*作为函数的第一个参数。

    因为Python不会对第一个星号做任何事情,但是它将知道*星号之后所有参数都应称为关键字参数(键-值对),也称为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
    

    第一个,大于或者等于ge,只能比较整数。例如大于等于1

    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
    

    第二个,小于或者等于le,只能比较整数。例如小于等于1000

    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
    

    第三个,大于gt, 小于lt,可以比较浮点数和整数

    from fastapi import FastAPI, Path, Queryapp = FastAPI()@app.get("/items/{item_id}")
    async def read_items(*,item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),q: str,size: float = Query(..., gt=0, lt=10.5) ):results = {"item_id": item_id}if q:results.update({"q": q})return results
    

    我们可以自由地混合使用PathQuery和请求主体参数声明,例如

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

    这里混合使用了路径参数item_id,查询参数q,请求体参数item。第一个参数星号表示星号之后所有参数都应称为关键字参数(键-值对),即使它们没有默认值。这是一种有效的写法。

    我们可以定义多个请求体模型,例如,ItemUser

    from fastapi import FastAPI
    from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Noneclass User(BaseModel):username: strfull_name: str = 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
    

    在这种情况下,请求体输入的格式是一个字典,它将使用参数名称作为正文中的key,Pydantic的类作为key的内容

    例如上述请求体的正确格式如下:

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

    我们将这个输入传入之后得到的输出如下,URL为:127.0.0.1:8001/items/3

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

    例如,扩展之前的模型,之前的模型还需要额外增加一个参数:除去itemuser这两个字段之外,还需要importance在请求体中,如果你直接定义它,因为他是一个单值,FastAPI会默认将其定义为query参数。

    但是你可以使用Body方法,让FastAPI将其视为请求体的key:

     importance: int = Body(...)
    
    from fastapi import Body, FastAPI
    from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Noneclass User(BaseModel):username: strfull_name: str = None@app.put("/items/{item_id}")
    async def update_item(*, item_id: int, item: Item, user: User, importance: 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
    }
    

    如果您想得到一个带有key的JSON,并且在key对应的包含模型内容,就像声明额外的主体参数时那样,则可以使用嵌入的特殊Body参数:

    from fastapi import Body, FastAPI
    from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = None@app.put("/items/{item_id}")
    async def update_item(*, item_id: int, item: 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
    }
    

    它对应的语法就是

    item: Item = Body(..., embed=True)
    

    将Body方法里面的参数embed设置为True

    我们可以使用Schema模型,在定义请求体模型的类属性的时候,给类属性添加默认值或者验证

    第一步,从pydantic导入Schema

    from pydantic import BaseModel, Schema 
    

    第二步,定义请求体模型并使用Schema模型

    from fastapi import Body, FastAPI
    from pydantic import BaseModel, Schemaapp = FastAPI()class Item(BaseModel):name: strdescription: str = Schema(None, title="The description of the item", max_length=300)price: float = Schema(..., gt=0, description="The price must be greater than zero")   tax: float = None@app.put("/items/{item_id}")
    async def update_item(*, item_id: int,item: Item = Body(..., embed=True)
    ):results = {"item_id": item_id, "item": item}return results
    

    注意一点SchemaQueryPathBody的工作方式相同,具有相同的参数

    将JSON模式示例字段传递给 body请求 JSON模式

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

    这样子的话,当我们访问/docs,我们将看到如下:Example Value | Schema 数据示例,告诉用户,这个api想要什么样格式的请求体格式数据。本例中是:

    {
    "name": "Foo", 
    "description": "A very nice Item", 
    "price": 35.4,
    "tax": 3.2,
    }
    

    ==我们可以将请求体的属性值类型设置为列表、元组,列表里面的元素可以是正常的数据类型,也可以是请求体模型。当然,请求体的属性值也可以设置为请求体模型。==这种现象我们称之为模型的嵌套,下面我们逐一来看这些情况。

    将属性值的类型定义为列表

    from fastapi import FastAPI
    from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Nonetags: list = [] 
    

    这样子我们没有定义列表里面元素的类型,我们进一步定义列表里面元素的类型为字符串,例如

    from typing import Listfrom fastapi import FastAPI
    from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = 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
    

    从中我们可以看出区别,首先我们需要先导入List

    from typing import List
    

    然后定义列表

     tags: List[str] = [] 
    

    当然,我们还可以将列表的元素类型定义为int或者其它类型。

     tags: List[int] = [] 
    

    或者是另外一个请求体模型

    from typing import List, Setfrom fastapi import FastAPI
    from pydantic import BaseModel, UrlStrapp = FastAPI()class Image(BaseModel):url: UrlStrname: strclass Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Nonetags: Set[str] = []  # 表示属性值的类型为元组,元组中元素的类型为字符串images: 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
    
     images: List[Image] = None 
    

    此时期望的请求体格式为:

    {"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"}]
    }
    

    这里解释一下,UrlStr表示url格式类型的字符串

    例如"http://example.com/baz.jpg",“http://example.com/dave.jpg”

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

    from typing import List, Setfrom fastapi import FastAPI
    from pydantic import BaseModel, UrlStrapp = FastAPI()class Image(BaseModel):url: UrlStrname: strclass Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Nonetags: Set[str] = []images: List[Image] = None class Offer(BaseModel):name: strdescription: str = Noneprice: floatitems: List[Item] @app.post("/offers/")
    async def create_offer(*, offer: Offer):return offer
    

    这个例子中,定义了三层嵌套模型。

    小结

    我们可以为请求体的属性值定义不同的类型,可以是列表,可以是元组,列表或者元组里面的元素可以是字符串或者是数值,还可以是请求体模型。这种结构给fastAPi传递参数带来很大的方便性。

    除了下面常见的几种数据类型之外,

    • int
    • float
    • str
    • bool

    fastAPI还提供了其它更复杂的数据类型

    数据类型
    1 UUID 一个标准的“通用唯一标识符”,在许多数据库和系统中通常作为ID使用。
    在请求和响应中将以str表示。
    2 datetime.datetime Python的日期时间类型: datetime.datetime.
    在请求和响应中,将以ISO 8601格式的str表示, 比如: 2008-09-15T15:53:00+05:00.
    3 datetime.date python的日期类型: datetime.date.
    在请求和响应中,将以ISO 8601格式的str表示, 比如: 2008-09-15.
    4 datetime.time Python的时间类型: datetime.time.
    在请求和响应中,将以ISO 8601格式的str表示, 比如: 14:23:55.003.
    5 datetime.timedelta Python的时间增量类型: datetime.timedelta.
    在请求和响应中,将以float表示总秒数.
    6 frozenset 在请求和响应中, 格式与 set相同:
    在请求中,将读取列表,消除重复,并将其转换为“集合”.
    在响应中,set将会被转化为list.
    7 bytes Python标准类型: bytes.
    在请求和响应中将被视为str。
    生成的Schema将指定它是带有binary“格式”的str
    8 Decimal Python标准类型: Decimal.
    在请求和响应中,将以float格式.

    下面是一些使用上面的数据类型的一些路径操作函数的例子:

    from datetime import datetime, time, timedelta 
    from uuid import UUID 
    from fastapi import Body, FastAPIapp = FastAPI()@app.put("/items/{item_id}")
    async def read_items(item_id: UUID,start_datetime: datetime = Body(None),end_datetime: datetime = Body(None),repeat_at: time = Body(None),process_after: timedelta = Body(None)
    ):pass
    

    例如,您可以执行常规的日期操作,例如:

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

    定义Cookie参数,就和你定义QueryPath参数一样的方式

    第一步、导入 Cookie:

    from fastapi import Cookie, FastAPI 
    

    第二步、声明Cookie参数

    第一个值是默认值,您可以传递所有其他验证或注释参数:

     ads_id: str = Cookie(None)
    
    from fastapi import Cookie, FastAPIapp = FastAPI()@app.get("/items/")
    async def read_items(*, ads_id: str = Cookie(None)
    ):return {"ads_id": ads_id}
    

    注意两点:

    • CookiePathQuery的姐妹类,它也同样继承自相同的Param类,所以适用路径参数和查询参数的验证,Cookie参数也适用
    • 申明cookies,你需要使用Cookie方法,否则参数会被解释为查询参数

    定义Header参数,就和你定义QueryPath参数一样的方式

    第一步、导入 Header:

    from fastapi import Cookie, FastAPI , Header 
    

    第二步、声明Header参数

    第一个值是默认值,您可以传递所有其他验证或注释参数:

     ads_id: str = Header(None)
    
    from fastapi import FastAPI, Headerapp = FastAPI()@app.get("/items/")
    async def read_items(*, user_agent: str = Header(None)):return {"User-Agent": user_agent}
    

    注意两点:

    • HeaderPathQueryCookie的姐妹类,它也同样继承自相同的Param类.,所以适用路径参数和查询参数的验证,Cookie参数也适用
    • 申明Header,你需要使用Header方法,否则参数会被解释为查询参数

    可以在任何路径操作中使用参数 response_model 声明用于响应的模型:

    • @app.get()
    • @app.post()
    • @app.put()
    • @app.delete()
    • etc.

    举例

    from typing import Listfrom fastapi import FastAPI
    from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str = Noneprice: floattax: float = Nonetags: List[str] = []@app.post("/items/", response_model=Item) async def create_item(item: Item):return item
    

    从上面的代码不难看出:

    • response_model是“ decorator”方法(get,post等)的参数,该参数接受一个模型类。它接收的类型与您为Pydantic模型属性声明的类型相同,因此它可以是Pydantic模型,但也可以是例如 一个Pydantic模型的清单,例如List [Item]。

    response_model功能:

    最重要的功能:将输出数据限制为模型的数据。从而可以将输出数据转换为其类型声明,并且可以验证数据最后将由自动文档系统进行使用

    第一种、我们可以将请求体的内容全部响应回去

    from fastapi import FastAPI
    from pydantic import BaseModel
    from pydantic.types import EmailStrapp = FastAPI()class UserIn(BaseModel):username: strpassword: stremail: EmailStrfull_name: str = None# Don't do this in production!
    @app.post("/user/", response_model=UserIn)
    async def create_user(*, user: UserIn):return user
    

    但很明显在实际运用中,我们不会希望将用户的密码也响应回去,所以出现第二种情况,将请求体的部分内容响应回去

    from fastapi import FastAPI
    from pydantic import BaseModel
    from pydantic.types import EmailStrapp = FastAPI()class UserIn(BaseModel):username: strpassword: stremail: EmailStrfull_name: str = Noneclass UserOut(BaseModel):username: stremail: EmailStrfull_name: str = None@app.post("/user/", response_model=UserOut) async def create_user(*, user: UserIn):return user
    

    其中不包含用户密码。如果你查看自动文档,你将会看到输入模型和输出模型都具有自己的JSON模式

    评论

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注

    站内搜索