基于Pydantic封装的通用模型在API请求验证中的应用详解

目录
  • 引言
  • 通用model
  • Mixin 模式
  • 组合模式
  • 结论
  • 杂谈

引言

Pydantic 是一个用于数据验证和解析的流行库,经常被用于 FastAPI 和其他现代 Python 项目中。在处理 API 请求时,我们经常需要对请求参数进行有效性检查,例如日期范围、分页和排序等。在本文中,我们将介绍如何在 Pydantic 中使用 Mixin 和组合模式来实现这些功能,并讨论它们的优缺点。

通用model

首先,我们定义了以下几个基础的 Pydantic 模型:

  • DateModel:用于表示日期范围,包含开始日期和结束日期。
  • OrderModel:用于表示排序参数,包含排序字段和排序方式(升序或降序)。
  • PageModel:用于表示分页参数,包含页码和每页数量。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @author: hui
# @Desc: { 通用的一些Pydantic模型 }
# @Date: 2023/03/30 11:57
from pydantic import BaseModel, Field, validator
from typing import Optional
from datetime import date

class DateModel(BaseModel):
    """日期模型"""

    start_date: Optional[date] = Field(None, description="开始日期")
    end_date: Optional[date] = Field(None, description="结束日期")

    @validator("end_date", always=True)
    def validate_end_date(cls, end_date, values):
        start_date = values.get("start_date")
        if all([start_date, end_date]) and end_date < start_date:
            raise ValueError("结束日期必须大于等于开始日期")
        return end_date

class OrderModel(BaseModel):
    """排序模型"""

    order_by: Optional[str] = Field(None, description="排序字段,逗号分隔")
    order_mode: Optional[str] = Field(None, description="排序方式,逗号分隔,asc升序desc降序")

    @validator("order_by", "order_mode", always=True)
    def split_comma_separated_string(cls, value):
        if value:
            return value.split(",")
        return value

    @validator("order_mode", always=True)
    def check_length(cls, order_mode, values):
        order_by = values.get("order_by")
        if order_by and order_mode and len(order_by) != len(order_mode):
            raise ValueError("order_by and order_mode must have the same length")
        return order_mode

class PageModel(BaseModel):
    """分页模型"""

    page: Optional[int] = Field(default=1, ge=1, description="页码")
    page_size: Optional[int] = Field(default=10, le=1000, description="每页数量, 默认10,最大1000")

接下来,我们通过混入(Mixin)和组合两种不同的方式将这些基础模型应用到一个实际的 API 请求中。

Mixin 模式

DateOrderModelMixin 类通过多重继承的方式继承了 DateModel 和 OrderModel。这种方式的优点是简单易懂,可以实现代码重用。然而,它也可能导致类层次结构变得复杂,尤其是当有多个 Mixin 之间存在依赖关系时。

class DateOrderModelMixin(DateModel, OrderModel):
    """日期与排序模型Mixin"""
    pass

组合模式

PageOrderModel 类通过组合的方式将 OrderModel 和 PageModel 作为它的属性。在初始化方法中,我们将请求参数映射到这两个模型,并调用基类的初始化方法。

组合模式的优点是代码结构更清晰,易于维护和扩展。但是,它可能需要编写更多的代码来将功能委托给组合的组件。

class PageOrderModel(BaseModel):
    """分页排序模型"""
    order_model: OrderModel = Field(OrderModel(), description="排序模型")
    page_model: PageModel = Field(PageModel(), description="分页模型")
    def __init__(self, **data):
        if "order_model" in data and "page_model" in data:
            order_model = data.pop("order_model", None)
            page_model = data.pop("page_model", None)
        else:
            # 用于直接平铺的字典入参
            order_params = {
                "order_by": data.pop("order_by", None),
                "order_mode": data.pop("order_mode", None),
            }
            page_params = {
                "page": data.pop("page", None),
                "page_size": data.pop("page_size", None),
            }
            order_model = OrderModel(**order_params)
            page_model = PageModel(**page_params)
        super().__init__(order_model=order_model, page_model=page_model, **data)
page_order = PageOrderModel(
    order_model=OrderModel(order_by="field1,field2", order_mode="asc,desc"),
    page_model=PageModel(page=1, page_size=10)
)
>>>out
order_model=OrderModel(order_by=['field1', 'field2'], order_mode=['asc', 'desc'])
page_model=PageModel(page=1, page_size=10)
req_params = {
    "order_by": "field1,field2",
    "order_mode": "asc,desc",
    "page": 1,
    "page_size": 10
}
req_model = PageOrderModel(**req_params)
>>>out
order_model=OrderModel(order_by=['field1', 'field2'], order_mode=['asc', 'desc'])
page_model=PageModel(page=1, page_size=10)

再来几个业务逻辑模型继承 DateOrderModelMixin 和 PageOrderModel 然后模拟一些请求参数去验证

让我们创建两个业务逻辑模型,一个用于查询商品信息,另一个用于查询订单信息。这两个模型分别继承 DateOrderModelMixin 和 PageOrderModel

from pydantic import BaseModel, Field
from typing import Optional

class ProductQueryModel(DateOrderModelMixin):
    product_category: Optional[str] = Field(None, description="商品类别")

class OrderQueryModel(PageOrderModel):
    customer_id: Optional[int] = Field(None, description="客户ID")

# 使用 ProductQueryModel 进行参数验证
product_query_params = {
    "start_date": "2023-04-01",
    "end_date": "2023-04-30",
    "order_by": "price",
    "order_mode": "desc",
    "product_category": "Electronics"
}

product_query = ProductQueryModel(**product_query_params)

>>>out
order_by=['price'] order_mode=['desc'] start_date=datetime.date(2023, 4, 1) end_date=datetime.date(2023, 4, 30) product_category='Electronics'

# 使用 OrderQueryModel 进行参数验证
order_query_params = {
    "start_date": "2023-04-01",
    "end_date": "2023-04-30",
    "order_by": "order_date",
    "order_mode": "asc",
    "page": 1,
    "page_size": 20,
    "customer_id": 12345
}

order_query = OrderQueryModel(**order_query_params)

>>>out
order_model=OrderModel(order_by=['order_date'], order_mode=['asc']) page_model=PageModel(page=1, page_size=20) customer_id=12345

这里的 ProductQueryModel 和 OrderQueryModel 分别用于处理商品查询和订单查询的请求参数。ProductQueryModel 继承自 DateOrderModelMixin,因此它具有日期范围和排序功能。OrderQueryModel 则继承自 PageOrderModel,具有分页和排序功能。

通过这两个模型,我们可以轻松地验证和解析传入的请求参数。在上面的示例代码中,我们分别创建了 product_query_params 和 order_query_params 字典来模拟请求参数,并使用 ProductQueryModel 和 OrderQueryModel 进行验证。可以看到,这两个模型成功解析了请求参数,并对日期范围、排序和分页进行了验证。

结论

在处理Pydantic模型时,根据具体的业务场景和需求来选择组合或Mixin模式。

Mixin模式适用于简单的继承关系,代码简洁易懂;组合模式适用于复杂的类关系,提供更好的灵活性和扩展性。在实际项目中,可以根据需求灵活选择这两种模式,或者根据情况将它们结合使用。

在实践中,如果需要将多个通用功能混合到一个业务逻辑模型中,Mixin模式可能是一个更好的选择,因为它可以让我们轻松地将这些功能组合在一起。然而,当我们需要对这些功能进行更精细的控制,或者在多个业务逻辑模型之间共享某些功能时,组合模式可能会更合适。

总之,在处理Pydantic模型时,我们应根据项目的实际需求和场景来权衡这两种模式的优缺点,从而做出合适的选择。这里的入参校验感觉使用多继承会更简单点,但到一些复杂的业务逻辑处理时可以使用组合模式,来做到更好的维护与扩展。

由于 GET 请求的入参不太好定义数据结构,减少的代码冗余就想到了多继承来组合属性和方法,如果使用 POST 请求传递 json 数据入参就可以更好设计参数结构,这时使用组合的方式hui更好。

杂谈

go的结构体嵌套就有点像组合

type Address struct {
    Street string
    City   string
    State  string
    Zip    string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

通过结构体的组合,可以方便地组合多个不同的数据结构,构建出更加复杂的结构体。这种组合方式可以让代码更加灵活和可维护,同时也可以提高代码的可读性和可重用性。

以上就是基于Pydantic封装的通用模型在API请求验证中的应用详解的详细内容,更多关于Pydantic封装API请求验证的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python编程pydantic触发及访问错误处理

    目录 常见触发错误的情况 错误的触发 注意 访问错误的方式 简单栗子 复杂栗子 自定义错误 自定义错误模板类 PydanticValueError 常见触发错误的情况 如果传入的字段多了会自动过滤 如果传入的少了会报错,必填字段 如果传入的字段名称对不上也会报错 如果传入的类型不对会自动转换 如果不能转换则会报错 错误的触发 pydantic 会在它正在验证的数据中发现错误时引发 ValidationError 注意 验证代码不应该抛出 ValidationError 本身 而是应该抛出 Val

  • python库pydantic的简易入门教程

    目录 一.简介 二.安装 三.常见模型 1.BaseModel 基本模型 2.递归模型 3.GenericModel 通用模型(泛型): 四.常用类型 五.验证器 六.配置 七.与 mypy 一起使用 总结 一.简介 pydantic 库是 python 中用于数据接口定义检查与设置管理的库. pydantic 在运行时强制执行类型提示,并在数据无效时提供友好的错误. 它具有如下优点: 与 IDE/linter 完美搭配,不需要学习新的模式,只是使用类型注解定义类的实例 多用途,BaseSett

  • pydantic-resolve嵌套数据结构生成LoaderDepend管理contextvars

    目录 pydantic-resolve 解决嵌套数据结构的生成和其他方案的比较 和GraphQL相比 和 ORM 的 relationship相比 LoaderDepend的用途 背景 解决方法 遗留问题 (已经解决) pydantic-resolve 解决嵌套数据结构的生成和其他方案的比较 pydantic-resolve 和GraphQL相比 GraphQL的优势是 1.方便构建嵌套结构,2.client可以方便生成查询子集.非常适合构建满足灵活变化的 public API的场景. 但是很多

  • C++利用MySQL API连接和操作数据库实例详解

    1.C++连接和操作MySQL的方式 系列文章: MySQL 设计和命令行模式下建立详解 C++利用MySQL API连接和操作数据库实例详解 在Windows平台,我们可以使用ADO.ODBC或者MySQL API进行连接和操作.ADO (ActiveX Data Objects,ActiveX数据对象)是Microsoft提出的一个用于存取数据源的COM组件.它提供了程序语言和统一数据访问方式OLE DB的一个中间层,也就是Microsoft提出的应用程序接口(API)用以实现访问关系或非关

  • 基于Java class对象说明、Java 静态变量声明和赋值说明(详解)

    先看下JDK中的说明: java.lang.Object java.lang.Class<T> Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class tha

  • 基于HttpClient在HTTP协议接口测试中的使用(详解)

    HTTP协议的接口测试中,使用到最多的就是GET请求与POST请求,其中POST请求有FORM参数提交请求与RAW请求,下面我将结合HttpClient来实现一下这三种形式: 一.GET请求: GET请求时,参数一般是写在链接上的,代码如下: public void get(String url){ CloseableHttpClient httpClient = null; HttpGet httpGet = null; try { httpClient = HttpClients.creat

  • Springboot使用redis进行api防刷限流过程详解

    这篇文章主要介绍了Springboot使用redis进行api防刷限流过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 api限流的场景 限流的需求出现在许多常见的场景中 秒杀活动,有人使用软件恶意刷单抢货,需要限流防止机器参与活动 某api被各式各样系统广泛调用,严重消耗网络.内存等资源,需要合理限流 淘宝获取ip所在城市接口.微信公众号识别微信用户等开发接口,免费提供给用户时需要限流,更具有实时性和准确性的接口需要付费. api限流实

  • 基于Python的身份证验证识别和数据处理详解

    根据GB11643-1999公民身份证号码是特征组合码,由十七位数字本体码和一位数字校验码组成,排列顺序从左至右依次为: 六位数字地址码八位数字出生日期码三位数字顺序码一位数字校验码(数字10用罗马X表示) 校验系统: 校验码采用ISO7064:1983,MOD11-2校验码系统(图为校验规则样例) 用身份证号的前17位的每一位号码字符值分别乘上对应的加权因子值,得到的结果求和后对11进行取余,最后的结果放到表2检验码字符值..换算关系表中得出最后的一位身份证号码 代码: # coding=ut

  • Java实现API sign签名校验的方法详解

    目录 1. 前言 2. 签名生成策略 3. API 签名算法 Java 实现 4. 测试一下 1. 前言 目的:为防止中间人攻击. 场景: 项目内部前后端调用,这种场景只需要做普通参数的签名校验和过期请求校验,目的是为了防止攻击者劫持请求 url 后非法请求接口. 开放平台向第三方应用提供能力,这种场景除了普通参数校验和请求过期校验外,还要考虑 3d 应用的授权机制,不被授权的应用就算传入了合法的参数也不能被允许请求成功. 2. 签名生成策略 接下来详述场景 2,其实场景 1 也包含在场景 2

  • Go语言异步API设计的扇入扇出模式详解

    目录 前言 扇入/扇出服务 Go 语言实现扇入/扇出模式 前言 扇出/扇入模式是更高级 API 集成的主要内容.这些应用程序并不总是表现出相同的可用性或性能特征. 扇出是从电子工程中借用的一个术语,它描述了输入的逻辑门连接到另一个输出门的数量.输出需要提供足够的电流来驱动所有连接的输入.在事务处理系统中,用来描述为了服务一个输入请求而需要做的请求总数. 扇入是指为逻辑单元的输入方程提供输入信号的最大数量.扇入是定义单个逻辑门可以接受的最大数字输入数量的术语.大多数晶体管-晶体管逻辑 (TTL)

  • SpringBoot使用swagger生成api接口文档的方法详解

    目录 前言 具体例子 maven配置 项目application.yml配置 springApplication添加swagger注解 在控制层添加swagger注解 前言 在之前的文章中,使用mybatis-plus生成了对应的包,在此基础上,我们针对项目的api接口,添加swagger配置和注解,生成swagger接口文档 具体可以查看本站spring boot系列文章: spring boot项目使用mybatis-plus代码生成实例 具体例子 maven配置 在使用之前,我们需要添加s

  • Spring框架基于注解的AOP之各种通知的使用与环绕通知实现详解

    目录 一.基于注解的AOP之各种通知的使用 二.基于注解的AOP之环绕通知 一.基于注解的AOP之各种通知的使用 1.在切面中,需要通过指定的注解将方法标识为通知方法 @Before:前置通知,在目标对象方法执行之前执行 @After:后置通知,在目标对象方法的finally子句中执行 @AfterReturning:返回通知,在目标对象方法返回值之后执行 @AfterThrowing:异常通知,在目标对象方法的catch子句中执行 声明重用写入点表达式 @Pointcut("execution

  • Yii2 RESTful中api的使用及开发实例详解

    什么是RESTful风格的API 对于各种客户端设备与服务端的通信,我们往往都通过API为客户端提供数据,提供某种资源.关于RESTful的概念,一查一大推,一两句也解释不清,姑且先按照我们通俗的理解:在众多风格.众多原则的API中,RESTful就是一套比较优秀的接口调用方式. Yii2如何实现RESTful风格的API 1.建立单独的应用程序 为了增加程序的可维护性,易操作性,我们选择新建一套应用程序,这也是为了和前台应用.后台应用区分开操作.有些人要嚷嚷了,为啥非得单独搞一套呢?如果你就单

随机推荐

其他