FastAPI简介

news2024/12/16 4:20:53

FastAPI简介

  • 一、FastAPI简介
  • 二、FastAPI安装
    • 2.1 使用pip安装FastAPI
    • 2.2 FastAPI的demo
    • 2.3 FastAPI的程序结构
  • 三、装饰器请求方法
  • 四、用户请求
    • 4.1 路径参数
      • 4.1.1 单个路径参数
      • 4.1.2 多个路径参数
      • 4.1.3 固定路径和路径参数的冲突
    • 4.2 查询参数
    • 4.3 默认参数
    • 4.4 可选参数
  • 五、请求体
    • 5.1 关于请求体
    • 5.2 实现请求体
    • 5.3 关于Pydantic的BaseModel类
    • 5.4 关于Pydantic.Field

一、FastAPI简介

FastAPI源码
FastAPI官方中文文档
FastAPI官方文档

Pydantic官方文档

二、FastAPI安装

2.1 使用pip安装FastAPI

  • 安装FastAPI
pip install fastapi
  • 安装FastAPI依赖库
    FastAPI的依赖库包括 Uvicorn、Pydantic 和 Starlette 库
pip install uvicorn[standard]
pip install pydantic
pip install starlette

可以安装所有依赖库

pip3 install fastapi[all]
  • 验证安装成功
import fastapi
print(fastapi.__version__)

2.2 FastAPI的demo

创建main.py的文件:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

在命令行运行如下指令:

uvicorn main:app --reload

可以看到如下输出:

INFO:     Will watch for changes in these directories: ['/root']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [3531390] using WatchFiles
INFO:     Started server process [3531392]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

在本机浏览器中输入: http://127.0.0.1:8000
打印出:"message": "Hello World" ,则表示安装成功。
在这里插入图片描述

Uvicorn 启动信息

  • INFO: Will watch for changes in these directories: ['/root']
    表示 Uvicorn 将监视 /root 目录下的文件变化以触发自动重载。
    这意味着如果在这个目录下对 Python 文件进行了任何改动,Uvicorn 将会重启应用。
  • INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
    告知用户 Uvicorn 已经启动并在本地地址 http://127.0.0.1:8000 上监听请求。
    这意味着您可以访问此 URL 来测试您的 API。
  • INFO: Started reloader process [3531227] using WatchFiles
    说明 Uvicorn 使用 WatchFiles 方法启动了一个后台进程来监控文件系统的变化,以便能够及时响应文件更新并触发应用的重新加载。
  • INFO: Started server process [3531230]
    显示 Uvicorn 已经启动了一个新的子进程来处理 HTTP 请求。
  • INFO: Waiting for application startup. 和 INFO: Application startup complete.:
    这两条日志表明 FastAPI 应用正在启动,并且已经成功完成了启动过程,现在可以接收来自客户端的请求。
  • 综上所述,已经正确设置了开发环境,并且 FastAPI 应用程序正在正常运行。
    开发时,只要保持终端窗口打开并且不要关闭 Uvicorn 进程,每次对 main.py 或者其他被监控的文件做出更改,Uvicorn 都会自动重新加载应用,使得最新的代码变更立即生效。
    如果要停止服务,只需按 CTRL+C 即可终止 Uvicorn 进程。
    此外,请注意,在生产环境中部署时应避免使用 --reload 选项,因为这可能会导致不必要的资源消耗和稳定性问题9。

Uvicorn 命令详解

uvicorn main:app --reload 是一个用于启动 FastAPI 应用程序的命令行语句,它结合了 Uvicorn 服务器与 FastAPI 框架的功能。
作用就是使用 Uvicorn 启动位于 main.py 文件中的 FastAPI 应用,并且开启了热重载模式以便于开发过程中快速迭代和测试
这条命令中的各个部分具有特定的意义:

  • uvicorn:这是调用 Uvicorn ASGI(异步网关接口)服务器的命令。
  • main:app:这部分指定了要运行的应用程序。
    • main 表示 Python 模块的名字,通常是文件名为 main.py 的模块(即 main.py 文件去掉扩展名);
    • app 则是指在这个模块中定义的一个 FastAPI 实例对象的名字。
      例如,在 main.py 文件中有如下代码 app = FastAPI(),那么这里的 app 就是指这个 FastAPI 应用实例。
  • --reload:这是一个选项参数,表示开启热重载功能。
    • 当启用此选项时,Uvicorn 会在检测到代码发生更改后自动重启服务器。
    • 这对于开发环境非常有用,因为它可以确保开发者在修改代码后无需手动重启服务器即可看到最新的更改效果。
    • 值得注意的是,在生产环境中不应该使用 --reload 选项,因为这会消耗更多资源并且可能不如预期稳定。
  • 在非默认端口上使用热重载功能时,可以通过配置启动命令或修改配置文件来实现
    • 命令:uvicorn main:app --host 127.0.0.1 --port 9000 --reload
    • –host 参数指定了监听的 IP 地址
    • –port 参数则定义了监听的端口号
    • –reload 选项开启了热重载功能

2.3 FastAPI的程序结构

from fastapi import FastAPI
import uvicorn

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)
  • Step1: 导包
 import uvicorn
from fastapi import FastAPI
  • Step2: 创建一个 FastAPI 实例,这个实例是应用程序的核心,所有的路由和配置都将围绕它进行
app = FastAPI()
  • Step3: 定义一个路径操作函数 root,它被装饰器 @app.get("/") 标记为根路径(/)的处理器
    这意味着当用户访问应用程序的根 URL 时,FastAPI 将调用此函数来处理请求。
    • 返回一个 JSON 响应,包含消息 “Hello World”。由于 FastAPI 支持异步定义,
    • 使用 async def 可以更好地利用其异步特性,从而提高性能。
@app.get("/")
async def root():
    return {"message": "Hello World"}
  • Step4: 检查当前模块是否作为主程序运行。如果是,则执行以下代码块。
    调用 uvicorn.run() 函数启动 Uvicorn 服务器,提供必要的参数如应用对象 (app)、主机地址 (host) 和端口 (port)
    • 参数 host="127.0.0.1" 表示服务器只接受来自本地计算机的连接;
    • 参数 port=8000 表示服务器将在端口 8000 上监听 HTTP 请求。
if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)
  • Step5: 查看运行结果
    在本机浏览器中输入: http://127.0.0.1:8000
    打印出:"message": "Hello World" ,则表示安装成功。
    在这里插入图片描述

装饰器函数的返回值

  • 返回 Python 数据类型
    当路由函数返回一个简单的 Python 数据类型时,例如字符串、整数或布尔值,FastAPI 会自动将它们转换成相应的 JSON 格式,并设置正确的 Content-Type 头信息为 application/json。
    这意味着如果返回的是字符串 “hello fastapi”,它将被当作 JSON 字符串返回给客户端。
  • 返回字典或列表
    返回字典或列表时,FastAPI 同样会将其序列化为 JSON。
    • 对于字典而言,键必须是字符串,而值可以是任何能被 JSON 序列化的数据类型;
    • 对于列表,则其元素也需满足同样的条件。
  • 返回 Pydantic 模型
    Pydantic 是一个用于数据验证及设定的库,它允许定义具有类型注解的数据模型。
    当从 FastAPI 路由中返回 Pydantic 模型实例时,框架会自动调用 jsonable_encoder 函数来处理复杂的数据类型(如日期时间、UUID 等),确保它们能够正确地被 JSON 序列化。
    这不仅简化了开发者的代码编写过程,还保证了 API 输出的一致性和准确性。
  • 使用自定义 JSONResponse
    有时候,开发者可能希望对响应进行更细粒度的控制,比如添加自定义头部或者设置不同的状态码。
    这时就可以使用 JSONResponse 类来构建响应对象。JSONResponse 继承自 Response 类,允许指定额外的参数如 status_code、headers 和 media_type 来定制响应行为

三、装饰器请求方法

装饰器包含8中HTTP请求方法:

  • @app.get()
    用于从服务器获取信息。
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

# 如果访问 http://127.0.0.1:8000/items/42
# 运行结果:
# {
#   "item_id": 42
# }

在这里插入图片描述

  • @app.post()
    用于向指定资源提交数据,常用于创建新资源。
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

@app.post("/items/")
async def create_item(item: Item):
    return item

# 如果发送 POST 请求到 http://127.0.0.1:8000/items/
# 并且请求体是 {"name": "Foo", "description": "A very nice Item", "price": 35.4}
# 运行结果:
# {
#   "name": "Foo",
#   "description": "A very nice Item",
#   "price": 35.4,
#   "tax": null
# }

在这里插入图片描述

  • @app.delete()
    用于删除指定资源。
if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)

在这里插入图片描述

  • @app.put()
    用于更新整个资源。
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}

# 如果发送 PUT 请求到 http://127.0.0.1:8000/items/789
# 并且请求体是 {"name": "Updated Foo", "price": 35.4}
# 运行结果:
# {
#   "item_id": 789,
#   "name": "Updated Foo",
#   "description": null,
#   "price": 35.4,
#   "tax": null
# }

在这里插入图片描述

  • @app.head()
    与 GET 类似,但不返回消息体,只用来获取响应头。
from fastapi import FastAPI

app = FastAPI()

@app.head("/items/")
async def head_items():
    # 只返回头部,没有主体内容
    pass

# 如果发送 HEAD 请求到 http://127.0.0.1:8000/items/
# 运行结果:
# HTTP响应头,例如:
# Content-Length: 0
# Content-Type: application/json
# Date: Sat, 14 Dec 2024 17:54:00 GMT
# Server: uvicorn
  • @app.patch()
    用于对资源进行部分更新。
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class ItemUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    price: float | None = None
    tax: float | None = None

@app.patch("/items/{item_id}")
async def patch_item(item_id: int, item_update: ItemUpdate):
    return {"item_id": item_id, **item_update.dict(exclude_unset=True)}

# 如果发送 PATCH 请求到 http://127.0.0.1:8000/items/567
# 并且请求体是 {"name": "Patched Foo"}
# 运行结果:
# {
#   "item_id": 567,
#   "name": "Patched Foo"
# }

在这里插入图片描述

  • @app.trace()
    用于追踪路径,通常由客户端发送请求,服务器将请求作为实体返回。
from fastapi import FastAPI

app = FastAPI()

@app.trace("/trace")
async def trace_endpoint():
    # 追踪请求
    pass

# 如果发送 TRACE 请求到 http://127.0.0.1:8000/trace
# 运行结果:
# 返回客户端发送的原始请求
  • @app.options()
    用于描述通信选项,即客户端可以对资源执行哪些方法。
from fastapi import FastAPI

app = FastAPI()

@app.options("/items/")
async def options_items():
    return {"Allow": "GET, POST, PUT, DELETE, PATCH, OPTIONS"}

# 如果发送 OPTIONS 请求到 http://127.0.0.1:8000/items/
# 运行结果:
# {
#   "Allow": "GET, POST, PUT, DELETE, PATCH, OPTIONS"
# }

在这里插入图片描述

四、用户请求

FastAPI允许开发人员通过路径参数或路径变量从API的端点URL中获取请求数据。
(路径参数或路径变量使URL呈现一些动态变化。)

路径参数包含一个值,该值成为由花括号{}指示的URL的一部分。
在URL中设置这些路径参数后,FastAPI要求通过应用类型提示来声明这些参数。

4.1 路径参数

4.1.1 单个路径参数

以下delete_username()服务是一个DELETE API方法,它使用username路径参数搜索用户登录记录以进行删除:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


@app.delete("/login/remove/{username}")
async def delete_username(username: str):
    if username == None:
        return {"message": "invalid username"}
    else:
        return {"message": "deleted username"}

# 如果发送 DELETE 请求到 http://127.0.0.1:8000/login/remove/Jack
# 运行结果:
# {
#     "message": "deleted username"
# }

在这里插入图片描述

4.1.2 多个路径参数

如果最左边的变量比最右边的变量更有可能填充值,则可以接受多个路径参数。
换句话说,最左边的变量的重要性将使得其处理比最右边的路径变量更相关,也更正确。
应用此标准是为了确保端点URL不会看起来像其他URL,防止产生冲突和混淆。

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/login/{username}/{password}")
async def combine_parameters(username: str, password: int):
    # 定义有效的用户名和密码
    valid_username = "Jack"
    valid_password = 123456
    
    # 检查提供的用户名和密码是否匹配预定义的有效值
    if username == valid_username and password == valid_password:
        combined_result = f"username is {username}, and password is {password}"
        return {"message": combined_result}
    else:
        # 如果提供的用户名或密码不正确,则抛出一个HTTP异常
        raise HTTPException(status_code=401, detail="Invalid username or password")
# 如果发送 DELETE 请求到 http://127.0.0.1:8000/login/Jack/123456
# 运行结果:
# {
#     "message": "username is Jack, and password is 123456"
# }

在这里插入图片描述

4.1.3 固定路径和路径参数的冲突

FastAPI对属于基本路径或具有不同子目录的顶级域路径的端点URL不友好。
当我们动态URL模式在分配特定路径变量时,看起来与其他固定端点URL相同时,就会发生这种情况。
这些固定URL在这些动态URL之后依次实现。

以下服务就是一个示例:

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/login/{username}/{password}")
async def combine_parameters(username: str, password: str):
    # 定义有效的用户名和密码
    valid_username = "Jack"
    valid_password = "123456"
    
    # 检查提供的用户名和密码是否匹配预定义的有效值
    if username == valid_username and password == valid_password:
        combined_result = f"username is {username}, and password is {password}"
        return {"message": combined_result}
    else:
        # 如果提供的用户名或密码不正确,则抛出一个HTTP异常
        raise HTTPException(status_code=401, detail="Invalid username or password")
        
@app.get("/login/detail/info")
async def login_info():
    return {"message": "username and password are needed"}
# 如果发送 DELETE 请求到 http://127.0.0.1:8000/login/detail/info
# 运行结果:
# {
#    "message": "username and password are needed"
# }

理论上,我们期望的输出如下:

# 如果发送 DELETE 请求到 http://127.0.0.1:8000/login/detail/info
# 运行结果:
# {
#    "message": "username and password are needed"
# }

实际得到的输出:
在这里插入图片描述
分析原因:
访问的url是http://127.0.0.1:8000/login/detail/info
实际访问的url是http://127.0.0.1:8000/login/{detail}/{info}
也就是说,固定路径的details和info路径目录,被视为username和password参数值。

解决办法:
首先应声明所有固定路径,然后再声明带有路径参数的动态端点URL。
上述示例中的login_info()服务应该在login_with_token()之前声明。

修改后的代码:

from fastapi import FastAPI, HTTPException

app = FastAPI()

        
@app.get("/login/detail/info")
async def login_info():
    return {"message": "username and password are needed"}
    
    
@app.get("/login/{username}/{password}")
async def combine_parameters(username: str, password: str):
    # 定义有效的用户名和密码
    valid_username = "Jack"
    valid_password = "123456"
    
    # 检查提供的用户名和密码是否匹配预定义的有效值
    if username == valid_username and password == valid_password:
        combined_result = f"username is {username}, and password is {password}"
        return {"message": combined_result}
    else:
        # 如果提供的用户名或密码不正确,则抛出一个HTTP异常
        raise HTTPException(status_code=401, detail="Invalid username or password"

运行结果和预期一致:
在这里插入图片描述

4.2 查询参数

查询参数是在端点URL结束后提供的键值对(key-value pair), 用问号?表示。
就像路径参数一样,它也保存请求数据,所有查询参数也在服务方法中声明。
API服务可以管理一系列由&分割的查询参数。

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/login/")
async def login(username: str, password: str):
    # 定义有效的用户名和密码
    valid_username = "Jack"
    valid_password = "123456"
    
    # 检查提供的用户名和密码是否匹配预定义的有效值
    if username == valid_username and password == valid_password:
        combined_result = f"username is {username}, and password is {password}"
        return {"message": combined_result}
    else:
        return {"message": error}

访问url:http://127.0.0.1:8000/login/?username=Jack&password=123456
在这里插入图片描述
该login服务使用username和password作为str类型的查询参数。

4.3 默认参数

API服务的查看参数和路径参数,都不是固定参数,可以为它们指定默认值,避免出现验证错误消息。
根据要求,分配的默认值通常是

  • 数字类型的0
  • 布尔类型的False
  • 字符串类型的空字符串
  • List类型的空列表
  • Dict类型的空字典
from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/login/")
async def login(username: str="Jack", password: str="123456"):
    # 定义有效的用户名和密码
    valid_username = "Jack"
    valid_password = "123456"
    
    # 检查提供的用户名和密码是否匹配预定义的有效值
    if username == valid_username and password == valid_password:
        combined_result = f"username is {username}, and password is {password}"
        return {"message": combined_result}
    else:
        return {"message": error}

# 如果发送 DELETE 请求到 http://127.0.0.1:8000/login
# 由于路径参数username和password有默认值,所以运行结果:
# {
#    "message": "username is Jack, and password is 123456"
# }

4.4 可选参数

如果API服务的路径不一定需要由用户提供,可以将这些路径或参数设置为可选参数。

要声明可选参数,需要从typeing模块中导入Optional类型,然后使用它来设置参数。
它应该使用方括号[]包装参数的假定数据类型,并且如果需要可以具有任何默认值。

from fastapi import FastAPI, HTTPException
from typing import Optional

app = FastAPI()

@app.get("/login/")
async def login(username: str, password: Optional[str] = None):
    # 定义有效的用户名
    valid_username = "Jack"
    
    # 检查提供的用户名
    if username == valid_username:
        combined_result = f"username is {username}"
        return {"message": combined_result}
    else:
        return {"message": error}
# 如果发送 GET 请求到 http://127.0.0.1:8000/login/?username=Jack
# 由于路径参数username和password有默认值,所以运行结果:
# {
#    "message": "username is Jack"
# }

在这里插入图片描述

五、请求体

5.1 关于请求体

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

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

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

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

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

5.2 实现请求体

  • Setp1:从pydantic中导入BaseModel
from pydantic import BaseModel
  • Setp2:创建它的子类以利用所有属性和行为
  • Setp3:将模型定义为参数

5.3 关于Pydantic的BaseModel类

什么是 Pydantic 的 BaseModel?
Pydantic 是一个 Python 库,它帮助开发者确保他们的应用程序接收到的数据是正确的。
它通过使用 Python 的类型提示(type hints)来自动验证传入的数据是否符合预期的格式和类型。
BaseModelPydantic 提供的一个类,所有的数据模型都是从BaseModel类继承下来的

为什么需要Pydantic 的 BaseModel?

如何使用Pydantic 的 BaseModel?
下面的代码示例展示了如何使用 Pydantic 来定义一个数据模型,并通过该模型对输入的数据进行验证和解析

from datetime import datetime
from pydantic import BaseModel, PositiveInt

class User(BaseModel):
    id: int  
    name: str = 'John Doe'  
    signup_ts: datetime | None  
    tastes: dict[str, PositiveInt]  

external_data = {
    'id': 123,
    'signup_ts': '2019-06-01 12:22',  
    'tastes': {
        'wine': 9,
        b'cheese': 7,  
        'cabbage': '1',  
    },
}

user = User(**external_data)  

print(user.id)  
#> 123
print(user.model_dump())  
"""
{
    'id': 123, 
    'name': 'John Doe',
    'signup_ts': datetime.datetime(2019, 6, 1, 12, 22),
    'tastes': {'wine': 9, 'cheese': 7, 'cabbage': 1},
}
  1. 定义 User 模型
from datetime import datetime
from pydantic import BaseModel, PositiveInt

class User(BaseModel):
    id: int  # 声明变量id是一个整型,必选字段
    name: str = 'John Doe'  # 声明变量name是str类型,默认值是'John Doe',必选字段
    signup_ts: datetime | None  # 声明变量signup_ts是一个时间类型,可选字段
    tastes: dict[str, PositiveInt]  # 声明变量tastes是字典类型,键是字符串类型的正整数,必选字段
  • 字段类型声明:在 User 类中,我们为每个字段指定了类型。
    • id 是必需的整数;
    • name 是字符串,默认值为 ‘John Doe’,意味着如果创建实例时没有提供这个字段,则会自动设置为默认值;
    • signup_ts 可以是 datetime 对象或 None,表示该字段是可选的;
    • tastes 是一个字典,键为字符串,值为正整数(PositiveInt),确保用户兴趣爱好评分不会为负数。
  • 类型注解与验证:
    • Pydantic 使用 Python 的类型注解来指定字段的数据类型。
    • 当创建 User 实例时,Pydantic 会根据这些注解自动验证传入的数据是否符合预期格式。
  1. 创建 User 实例并验证数据
external_data = {
    'id': 123,
    'signup_ts': '2019-06-01 12:22',  
    'tastes': {
        'wine': 9,
        b'cheese': 7,  
        'cabbage': '1',  
    },
}

user = User(**external_data)
  • 数据转换:即使某些字段的值不是严格意义上的正确类型
    例如,b'cheese' 是字节串而不是字符串,'1' 是字符串而不是整数),Pydantic 也会尝试将它们转换成正确的类型。
    对于 signup_ts 字段,Pydantic 能够识别 ISO8601 格式的日期时间字符串,并将其转换为 datetime 对象。
  • 错误处理:如果数据不符合预期类型或者违反了约束条件(如非正整数),Pydantic 将抛出 ValidationError 异常,并给出详细的错误信息。
    在这个例子中,所有的数据都被成功地验证和转换了。
  1. ”解包”(unpacking)操作符 **
    user = User(**external_data) 是一种非常常见的 Python 语法,它使用了所谓的“解包”(unpacking)操作符**
    这种用法允许我们将一个字典中的键值对作为关键字参数传递给函数或类的构造方法
    在这个例子中, User 是由 Pydantic 定义的一个数据模型类,而 external_data 是一个包含键值对的字典,这些键名与 User 类定义的字段名称相匹配。
  • external_data 字典
    这是一个包含用户信息的字典,其中键对应于 User 模型类中的字段名,值则是相应的数据。

  • ** 解包操作符
    通过在字典前加上双星号 **,Python 会将字典里的每个键值对展开成单独的关键字参数,并传递给 User 类的构造函数。
    因此,上面的例子等价于如下代码:

user = User(id=123, signup_ts='2019-06-01 12:22', tastes={'wine': 9, 'cheese': 7, 'cabbage': '1'})
  1. 输出结果
print(user.id)  
#> 123
print(user.model_dump())  
"""
{
    'id': 123,
    'name': 'John Doe',
    'signup_ts': datetime.datetime(2019, 6, 1, 12, 22),
    'tastes': {'wine': 9, 'cheese': 7, 'cabbage': 1},
}
"""
  • 访问属性:一旦创建了 User 实例,就可以像普通 Python 对象一样访问其属性。
    这里打印了用户的 ID 和整个模型的字典表示形式。
    注意,在最终输出的结果中,signup_ts 已经被正确解析为了 datetime 对象,而 tastes 中的键也已经被统一成了字符串类型,且所有值都被转换为了正整数。
  1. 验证错误
 # continuing the above example...

from datetime import datetime
from pydantic import BaseModel, PositiveInt, ValidationError


class User(BaseModel):
    id: int
    name: str = 'John Doe'
    signup_ts: datetime | None
    tastes: dict[str, PositiveInt]


external_data = {'id': 'not an int', 'tastes': {}}  

try:
    User(**external_data)  
except ValidationError as e:
    print(e.errors())
    """
    [
        {
            'type': 'int_parsing',
            'loc': ('id',),
            'msg': 'Input should be a valid integer, unable to parse string as an integer',
            'input': 'not an int',
            'url': 'https://errors.pydantic.dev/2/v/int_parsing',
        },
        {
            'type': 'missing',
            'loc': ('signup_ts',),
            'msg': 'Field required',
            'input': {'id': 'not an int', 'tastes': {}},
            'url': 'https://errors.pydantic.dev/2/v/missing',
        },
    ]
    """

如果验证失败,Pydantic 将引发错误并详细说明错误原因:

external_data 包含了两个可能导致验证失败的问题:

  • id 字段的值 'not an int' 不是有效的整数。
    id 字段要求的是一个整数值,但是给定的是一个无法解析成整数的字符串 'not an int'
    因此,Pydantic 抛出了一个类型为 int_parsing 的错误,表明输入不是有效的整数,并且无法将字符串解析为整数

  • signup_ts 字段没有提供,默认情况下它是必需的,除非你在模型配置中指定了它可以为 None 或者设置了默认值。
    signup_ts 字段是必需的,但在 external_data 中并没有提供相应的值。
    由于它既不是一个有效的 `datetime`` 对象也不是 None(假设允许为空),所以 Pydantic 认为此字段缺失,从而抛出了 missing 类型的错误。

5.4 关于Pydantic.Field

  • 什么是 Pydantic.Field?
    Pydantic Field 是一个非常强大的工具,它允许开发者为数据模型中的字段添加额外的验证和元数据信息
    通过使用 Field,我们可以更精细地控制数据模型的行为,确保数据的有效性和一致性。

  • 为什么需要 Pydantic.Field?
    在定义数据模型时,有时仅靠类型注解并不能完全表达我们对字段的所有要求。
    例如,我们可能希望限制字符串的最大长度、指定数值的取值范围或为字段提供描述性文本。
    此时,Field 就派上了用场。
    它可以用来设置字段的默认值、定义必填项与可选项、设定最大最小值等约束条件,以及为字段添加描述信息,这些都对于生成文档和支持API开发非常重要。

  • 如何使用 Pydantic.Field?
    要使用 Field,首先需要从 pydantic 中导入它。
    然后,在定义模型类时,可以通过将 Field 函数作为字段类型的默认值来应用这些额外的信息和约束。
    下面是一个简单的例子:

from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str
    description: str = Field(None, title="The description of the item", max_length=10)
    price: float = Field(..., gt=0, description="The price must be greater than zero")
    tax: float = None

在这个例子中:
- description 字段被标记为可选,并且设置了最大长度为 10。
- price 字段是必填项(用省略号 … 表示),并且指定了它的值必须大于零。
- 当创建 Item 实例时,如果提供了超出规定的 description 长度或者不符合 price 约束的数据,则会触发验证错误。

  • Field 的参数说明?
    Field 提供了一系列参数用于定制字段行为,包括但不限于以下几种:
    • default : 定义字段的默认值;如果未提供该值,默认为 None。
    • alias : 定义字段的别名,这在处理不符合 Python 变量命名规则的字段名时特别有用。
    • title : 为字段定义标题,这对生成文档很有帮助。
    • description : 提供字段的描述信息,同样有助于生成详细的 API 文档。
    • min_length max_length : 对于字符串类型字段,可以定义其最小和最大长度。
    • gt, ge, lt, le : 分别表示数值类型的字段应大于、大于等于、小于、小于等于某个特定值。
    • 其他如 regex 正则表达式验证、allow_mutation 是否允许修改字段值等高级选项也都可以通过 Field 来配置。
    • 此外, Field 还支持传递任意关键字参数到 JSON Schema 中,这意味着你可以根据需要灵活地扩展字段的功能。
      例如, examples 参数可以直接影响生成的 OpenAPI 文档中的示例值。

总之, Field 不仅仅是用来替代简单类型注解的一种方式,它更是 Pydantic 框架中实现复杂数据验证逻辑不可或缺的一部分。

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

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

相关文章

Django结合websocket实现分组的多人聊天

其他地方和上一篇大致相同,上一篇地址点击进入, 改动点1:在setting.py中最后再添加如下配置: # 多人聊天 CHANNEL_LAYERS {"default":{"BACKEND": "channels.layers.InMemoryChannelLayer"} }因此完整的se…

基础学习:(5)不同卷积:transposed convolution,deconvolution,dilated convolution

基础学习:(5)不同卷积 文章目录 基础学习:(5)不同卷积前言1 deconvlution transposed convolution2 对比2.1 Convolution animations2.2 Transposed convolution animations2.3 Dilated convolution 前言 …

Windows安装Jira

下载 Download Jira Data Center | Atlassian https://product-downloads.atlassian.com/software/jira/downloads/atlassian-jira-software-10.3.0-x64.exe 以管理员身份安装,否则弹出以下提醒 创建和配置MySQL数据库:参照 Connecting Jira applicat…

采用qL-MPC技术进行小型固定翼无人机的路径跟随控制

来自论文"Predictive Path-Following Control for Fixed-Wing UAVs Using the qLMPC Framework in the Presence of Wind Disturbances" 控制架构 采用的是 ULTRA-Extra无人机,相关参数如下: 这里用于guidance law的无人机运动学模型为&#…

计算机毕设-基于springboot的青少年心理健康教育网站的设计与实现(附源码+lw+ppt+开题报告)

博主介绍:✌多个项目实战经验、多个大型网购商城开发经验、在某机构指导学员上千名、专注于本行业领域✌ 技术范围:Java实战项目、Python实战项目、微信小程序/安卓实战项目、爬虫大数据实战项目、Nodejs实战项目、PHP实战项目、.NET实战项目、Golang实战…

28.在 Vue 3 中使用 OpenLayers 加载 MVT 格式矢量瓦片数据并显示图形

前言 随着前端开发技术的不断进步,越来越多的强大地图库被广泛应用于 Web 地图应用开发中。OpenLayers 是一个流行的开源 JavaScript 库,能够帮助开发者快速构建交互式地图应用。而 Vue 3 作为现代化的前端框架,已经成为开发者构建高效、响应…

Linux - MySQL迁移至一主一从

Linux - MySQL迁移至一主一从 迁移准备安装MySQL ibd文件迁移原服务器操作目标服务器操作 一主一从增量同步异常解决结尾 首先部分单独安装MySQL,请参考Linux - MySQL安装,迁移数据量比较大约400G左右且网络不通故使用文件迁移,需开启一段时间…

opencv-python的简单练习

题目1.读取一张彩色图像并将其转换为灰度图。 import cv2 # 读取图片文件 img cv2.imread(./1.png)# 将原图灰度化 img_gray cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 输出图片 cv2.imshow(img,img) cv2.imshow(img_g,img_gray) # 进行阻塞 cv2.waitKey(0) 题目2:…

go-zero(十三)使用MapReduce并发

go zero 使用MapReduce并发 一、MapReduce 介绍 MapReduce 是一种用于并行计算的编程模型,特别适合在大规模数据处理场景中简化逻辑代码。 官方文档: https://go-zero.dev/docs/components/mr 1. MapReduce 的核心概念 在 MapReduce 中,主…

探索React与Microi吾码的完美结合:快速搭建项目,低代码便捷开发教程

一、摘要 在当今的数字化时代,软件开发就像是一场探险,每个开发者都是探险家,探索着代码的奥秘。React作为前端开发的领军框架,其组件化和高效的渲染机制为开发者提供了强大的工具。而Microi吾码低代码平台的出现,则为…

SAP FICO物料分类账实操

物料分类账所涉及到的差异从采购入库的时候就可能已经产生,接下来从创建物料主数据开始对可能产生差异地方进行分析。其中有些操作步骤在标准价格估算这一篇博文中已经有过演示,可以先做了解。 其中的某些创建在有直接可用的情况下是非必须的&#xff0…

WordPress酱茄主题 开源版 博客资讯自媒体网站模板

一款免费开源的WordPress主题,主题专为WordPress博客、资讯、自媒体网站而设计 运行环境 支持WordPress版本:5.6 兼容Chrome、Firefox、Safari等主流浏览器 支持设备:响应式布局,不同设备不同展示效果 服务器环境建议&#x…

【HF设计模式】03-装饰者模式

声明:仅为个人学习总结,还请批判性查看,如有不同观点,欢迎交流。 摘要 《Head First设计模式》第3章笔记:结合示例应用和代码,介绍装饰者模式,包括遇到的问题、遵循的 OO 原则、达到的效果。 …

Linux查看是否有www-data用户,如果没有添加一个

在 Linux 系统中,www-data 用户通常是用来运行 Web 服务(如 Nginx 或 Apache)的。如果你想检查系统中是否已经存在 www-data 用户,并在没有的情况下添加一个,可以按照以下步骤操作: ### 1. 检查 www-data …

23.模块和包

模块 模块Module,是一个python文件,以.py结尾。 模块能定义函数、类和变量。 模块导入 模块在使用前需要先导入 [from 模块名] import [模块 | 类 | 变量 | 函数 | *] [as 别名] import 模块 import time print("start...") time.sleep(5) print(&…

IDEA报错:无效的源发行版、无效的目标发行版

1. 无效的源发行版 创建项目的时候,会遇见这个报错,原因就是编译的JDK版本与发布版本不一致。 解决方法: 1.1. 找到问题所在地 英文:File -> Project Structure ->Project Settings 中文:文件->项目结构 …

2025年,客服知识库与人工智能的结合

随着人工智能(AI)技术的飞速发展,传统客服模式正在经历前所未有的变革。特别是在2025年,客服知识库与AI的深度融合,不仅极大地提升了客服处理的效率与准确性,还为用户带来了更加个性化、高效的服务体验。 …

JVM 双亲委派模型以及垃圾回收机制

目录 1. JVM 内存区域划分 2. JVM 中类加载的过程 1) 类加载的基本流程 2) 双亲委派模型 3. JVM 中垃圾回收机制 1) 找到垃圾 a) 引用计数 b) 可达性分析 2) 释放垃圾 1. JVM 内存区域划分 一个运行起来的 Java 进程,其实就是一个 JVM 虚拟机。 而进程是…

微信小程序跳转其他小程序以及跳转网站

一、跳转其他小程序 1.1 知道appid和页面路径 wx.navigateToMiniProgram({appId: appid, // 替换为目标小程序 AppIDpath: pathWithParams, // 小程序路径envVersion: release, // 开发版、体验版或正式版success(res) {console.log("跳转到其他小程序成功!&q…

学习笔记:从ncsi/nc-si协议和代码了解网络协议的设计范式

学习笔记:从ncsi/nc-si协议和代码了解网络协议的设计范式 参考文档: https://www.dmtf.org/standards/published_documents https://www.dmtf.org/dsp/DSP0222 https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.2.0.pdf参考代…