使用 schema 库,自定义较复杂的校验方法

news2025/1/13 2:39:10

目录

  • 一、前置说明
    • 1、总体目录
    • 2、相关回顾
    • 3、本节目标
  • 二、操作步骤
    • 1、项目目录
    • 2、依赖包安装及说明
    • 3、代码实现
    • 3、测试代码
    • 4、日志输出
  • 三、后置说明
    • 1、要点小结
    • 2、下节准备

一、前置说明

1、总体目录

  • 《 pyparamvalidate 参数校验器,从编码到发布全过程》

2、相关回顾

  • 添加 常用校验方法,校验常见数据格式
  • 添加 自定义校验方法,让用户自定义校验规则

3、本节目标

  • 了解 schema 库的基本用法
  • 使用 schema 库,自定义较复杂的校验方法

二、操作步骤

1、项目目录

  • atme : @me 用于存放临时的代码片断或其它内容。
  • pyparamvalidate : 新建一个与项目名称同名的package,为了方便发布至 pypi
  • core : 用于存放核心代码。
  • tests : 用于存放测试代码。
  • utils : 用于存放一些工具类或方法。

2、依赖包安装及说明

pip install schema
  • schema 是一个轻量级的库,用于数据验证和结构定义。它支持定义嵌套的结构,并且可以进行复杂的验证。
  • schema GitHub 仓库地址:https://github.com/keleshev/schema

3、代码实现

demo


...

class Validator(metaclass=RaiseExceptionMeta):
    def __init__(self, value, field=None, rule_des=None):
        self.value = value
        self._field = field
        self._rule_des = rule_des

    def schema_validate(self, schema: Schema) -> Self:
        """
        schema 官方参考文档: https://pypi.org/project/schema/

        下面是涵盖了 Schema 大部分特性的示例说明:

        1. 定义 schema

            # 自定义处理函数,首字母大写
            def capitalize(value):
                return value.capitalize()


            # 邮箱格式验证函数
            def validate_email(value):
                email_regex = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
                return bool(re.match(email_regex, value))


            user_schema = schema.Schema({
                'username': schema.And(str, lambda s: len(s.strip()) > 0, error='Username cannot be empty or contain only spaces'),
                'phone_number': schema.Regex(r'^\d{11}$', error='Invalid phone number format. It should be a 10-digit number.'),
                'email': schema.And(schema.Or(str, None), lambda s: validate_email(s) if s is not None else True, error='Invalid email format'),
                'age': schema.And(int, lambda n: 0 <= n <= 120, error='Age must be an integer between 0 and 120'),
                'gender': schema.And(str, lambda s: s.lower() in ['male', 'female', 'other'], error='Invalid gender'),
                'family_members': schema.And(schema.Use(list), [schema.Use(capitalize)]),
                'others': {
                    'address': schema.And(str, lambda s: s.strip(), error='Address must be a non-empty string'),
                    'blog': schema.Or(None, schema.Regex(r'^https?://\S+$', error='Invalid blog format. It should be a valid URL starting with http:// or https://')),
                    'other': schema.Or(str, None)
                }
            })

        2. 使用 schema 进行校验

            valid_data = {
            'username': 'JohnDoe',
            'phone_number': '13888886666',
            'email': 'john@example.com',
            'age': 25,
            'gender': 'male',
            'family_members': ['Alice', 'Bob', 'Charlie'],
            'others': {
                'address': '123 Main St',
                'blog': 'http://example.com',
                'other': 'Some additional info'
                }
            }

            Validator(valid_data).schema_validate(user_schema)

        """
        if not isinstance(schema, Schema):
            raise CallValidateMethodError(f'{schema} must be a instance of Schema, not {type(schema)}, '
                                          f'Please use "schema.Schema()" to initialize the schema.'
                                          f'Documentation: https://pypi.org/project/schema/')

        # 将 validate 之后的值赋值给 self.value,因为 schema 在校验的过程中可以对 value 进行预处理
        self.value = schema.validate(self.value)
        return self

	...
        

3、测试代码

pyparamvalidate/tests/test_validator.py


import os
import re

import pytest
import schema

from pyparamvalidate.core.validator import Validator, CallValidateMethodError


# 自定义处理函数,首字母大写
def capitalize(value):
    return value.capitalize()


# 邮箱格式验证函数
def validate_email(value):
    email_regex = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
    return bool(re.match(email_regex, value))


reference_correct_data = {
    'username': 'JohnDoe',
    'phone_number': '13888886666',
    'email': 'john@example.com',
    'age': 25,
    'gender': 'male',
    'family_members': ['Alice', 'Bob', 'Charlie'],
    'others': {
        'address': '123 Main St',
        'blog': 'http://example.com',
        'other': 'Some additional info'
    }
}

user_schema = schema.Schema({
    'username': schema.And(str, lambda s: len(s.strip()) > 0, error='Username cannot be empty or contain only spaces'),
    'phone_number': schema.Regex(r'^\d{11}$', error='Invalid phone number format. It should be a 10-digit number.'),
    'email': schema.And(schema.Or(str, None), lambda s: validate_email(s) if s else True, error='Invalid email format'),
    'age': schema.And(int, lambda n: 0 <= n <= 120, error='Age must be an integer between 0 and 120'),
    'gender': schema.And(str, lambda s: s.lower() in ['male', 'female', 'other'], error='Invalid gender'),
    'family_members': schema.And(schema.Use(list), [schema.Use(capitalize)]),
    'others': {
        'address': schema.And(str, lambda s: s.strip(), error='Address must be a non-empty string'),
        'blog': schema.Or(None, schema.Regex(r'^https?://\S+$', error='Invalid blog format. It should be a valid URL starting with http:// or https://')),
        'other': schema.Or(str, None)
    }
})


def test_schema_validate_01():
    valid_data = {
        'username': 'JohnDoe',
        'phone_number': '13888886666',
        'email': 'john@example.com',
        'age': 25,
        'gender': 'male',
        'family_members': ['Alice', 'Bob', 'Charlie'],
        'others': {
            'address': '123 Main St',
            'blog': 'http://example.com',
            'other': 'Some additional info'
        }
    }
    assert Validator(valid_data).schema_validate(user_schema).value == valid_data


# 反向测试用例
def test_schema_validate_02():
    invalid_data_list = [
        # 无效的用户名(空字符串)
        {'username': '   ', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'],
         'others': {'address': '123 Main St', 'other': 'Some additional info'}},
        # 无效的邮箱格式
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'invalidemail', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'],
         'others': {'address': '123 Main St', 'other': 'Some additional info'}},
        # 无效的年龄(负数)
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': -5, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'],
         'others': {'address': '123 Main St', 'other': 'Some additional info'}},
        # 无效的性别
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': 25,
         'gender': 'unknown',
         'family_members': ['Alice', 'Bob', 'Charlie'],
         'others': {'address': '123 Main St', 'other': 'Some additional info'}},
        # 无效的博客格式(不是以 http:// 或 https:// 开头)
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'], 'others': {'address': '123 Main St', 'blog': 'invalidblog',
                                                                   'other': 'Some additional info'}},
        # 无效的家庭成员列表(包含空字符串)
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', '', 'Charlie'],
         'others': {'address': '123 Main St', 'other': 'Some additional info'}},
        # 无效的地址(空字符串)
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'], 'others': {'address': '', 'other': 'Some additional info'}},
        # 无效的电话号码格式(不是10位数字)
        {'username': 'JohnDoe', 'phone_number': '12345', 'email': 'john@example.com', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'],
         'others': {'address': '123 Main St', 'other': 'Some additional info'}},
        # 无效的博客格式(None 但不为空字符串)
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'],
         'others': {'address': '123 Main St', 'blog': '', 'other': 'Some additional info'}},
        # 无效的博客格式(不是以 http:// 或 https:// 开头)
        {'username': 'JohnDoe', 'phone_number': '13888886666', 'email': 'john@example.com', 'age': 25, 'gender': 'male',
         'family_members': ['Alice', 'Bob', 'Charlie'], 'others': {'address': '123 Main St', 'blog': 'invalidblog',
                                                                   'other': 'Some additional info'}}
    ]

    for invalid_data in invalid_data_list:
        with pytest.raises(schema.SchemaError) as exc_info:
            Validator(invalid_data).schema_validate(user_schema)
        print(exc_info.value)
        print('===============================')
        

4、日志输出

执行 test 的日志如下,验证通过:

============================= test session starts =============================
collecting ... collected 29 items

test_validator.py::test_schema_validate_01 PASSED                        [  3%]
test_validator.py::test_schema_validate_02 PASSED                        [  6%]Username cannot be empty or contain only spaces
===============================
Invalid email format
===============================
Age must be an integer between 0 and 120
===============================
Invalid gender
===============================
Invalid blog format. It should be a valid URL starting with http:// or https://
===============================
Key 'others' error:
Missing key: 'blog'
===============================
Address must be a non-empty string
===============================
Invalid phone number format. It should be a 10-digit number.
===============================
Invalid blog format. It should be a valid URL starting with http:// or https://
===============================
Invalid blog format. It should be a valid URL starting with http:// or https://
===============================

三、后置说明

1、要点小结

  • schema 是一个轻量强大的库,可以用于一些较复杂的数据校验。
  • schema_validate 中的示例说明,涵盖了 schema 的大部分特性,可用于参考。
  • 官方文档,请访问:https://github.com/keleshev/schema

2、下节准备

  • 至此,pyparamvalidate 的核心部分已基本完成。
  • 下一步,将本项目 pyparamvalidate ,发布至 pypi
  • 如果有迭代优化,将会持续更新。

点击返回主目录

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

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

相关文章

记录浏览器莫名其妙部分网页无法访问的一个解决办法

问题描述&#xff1a; 不知道什么原因&#xff0c;浏览器无法访问CSDN了&#xff0c;访问其他网站都可以正常加载。 经电脑网络诊断检测&#xff0c;反馈内容大致为&#xff1a; 资源处于联机状态但未对连接尝试做出响应&#xff0c;远程计算机不接受端口443上的连接。 测试…

如何搭建开源知识库软件AFFiNE并实现公网环境远程协作【内网穿透】

目录 前言 1. 使用Docker安装AFFINE 2. 安装cpolar内网穿透工具 3. 配置AFFINE公网访问地址 4. 实现公网远程访问AFFINE 结语 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊如何搭建开源知识库软件AFFiNE并实现公网环境远程协作【内网穿…

用java实现Client和Server之间的互相通信

概要&#xff1a;看过我之前文章的人都知道&#xff0c;client和server之间的通信必不可少的就是socket。而java已经帮我们做了很多事情。 创建Server端 第一步&#xff0c;创建ServerSocket 这个从名字上就可以看出来&#xff0c;服务器上的socket 0.0 ServerSocket ser…

ROS2学习笔记三:话题Topic

目录 前言 1 话题简介 2 常用指令 3 RCLCPP实现实现话题 3.1 创建工作空间 3.2 代码编写 3.2.1 发布端编写 3.2.2 发布端编写 前言 ROS2中的一个重要概念是话题&#xff08;Topic&#xff09;。话题是一种通过发布者和订阅者之间进行异步通信的机制。发布者&#xff0…

Java常用类---Math类和Random类

Math类 简介 Java中&#xff0c;Math类包含了用于执行基本数学运算的属性和方法。Math类的方法都被定义为static形式(静态方法)&#xff0c;通过Math类可以直接在主函数中直接调用。 如下图所示&#xff0c;Math.PI等于圆周率π、Math.E等于常量e……等属性和方法。 部分Mat…

主动学习基础-贝叶斯神经网络

引言 传统的深度神经网络一般都有过度自信的问题。 即使我给神经网络提供一个从来没有训练过的类别图像&#xff0c;神经网络也会输出一个类别。比如训练猫狗的分类器&#xff0c;如果你抛出一个人的图像&#xff0c;网络也会将其分类为猫或者狗。 在几乎所有现实世界的问题中…

详解java中ArrayList

目录 前言 一、ArrayList是什么 二、ArrayList使用 1、ArrayList的构造 2 、ArrayList常见操作 3、 ArrayList的遍历 4、 ArrayList的扩容机制 三、来个练习 前言 当你看到这篇文章我觉得很好笑&#xff0c;因为我开始也不懂ArrayList现在轮到你了&#xff0c;嘻嘻嘻&am…

深入解析HubSpot在线客户互动工具:提升客户体验的利器

在数字化时代&#xff0c;客户体验成为企业成功的关键因素之一。HubSpot作为一体化的市场营销、销售和服务平台&#xff0c;其在线客户互动工具扮演着提升客户体验的重要角色。本文将深入探讨HubSpot的在线客户互动工具&#xff0c;包括实时聊天、机器人和社交媒体监控&#xf…

视频号的视频怎么提取?推荐2种方法让广告人下载高清原视频变得更轻松

​在当今这个视觉主导的时代&#xff0c;身为一名广告人&#xff0c;您是否经常还在烦恼视频号的视频怎么提取&#xff1f;并能快速、高效地下载到高质量的原视频素材而头疼呢&#xff1f; 视频号提取助手 现在&#xff0c;问题有了答案——"视频号提取助手"这款神…

【K8S 存储卷】K8S的存储卷+PV/PVC

目录 一、K8S的存储卷 1、概念&#xff1a; 2、挂载的方式&#xff1a; 2.1、emptyDir&#xff1a; 2.2、hostPath&#xff1a; 2.3、NFS共享存储&#xff1a; 二、PV和PVC&#xff1a; 1、概念 2、请求方式 3、静态请求流程图&#xff1a; 4、PV和PVC的生命周期 5、…

怎样无货源开网店?2024抖店最新开通和运营教程,小白必看!

我是王路飞。 无货源模式因为对货源没有要求&#xff0c;也算是新手小白入局电商平台唯一的模式选择了。 那么怎么开通无货源网店呢&#xff1f;以抖店举例。 2024抖店最新的开通和运营教程分享如下&#xff0c;小白必看&#xff01; 内容来源于【醒醒团队-电商王路飞】 无…

如何正确选择ESD保护二极管

ESD保护二极管是一种齐纳二极管&#xff0c;专门用来保护电路免受过压浪涌&#xff0c;特别是静电放电&#xff08;ESD&#xff09;事件的影响。 当二极管反向偏置时&#xff0c;有很少的电流从阴极流向阳极。然而&#xff0c;当反向偏压超过某一点&#xff08;称为反向击穿电压…

【动态规划】【 数学】C++算法:514自由之路

作者推荐 【动态规划】458:可怜的小猪 涉及知识点 动态规划 数学 力扣514 自由之路 电子游戏“辐射4”中&#xff0c;任务 “通向自由” 要求玩家到达名为 “Freedom Trail Ring” 的金属表盘&#xff0c;并使用表盘拼写特定关键词才能开门。 给定一个字符串 ring &#x…

APP备案流程

一、 APP备案是指 自2000年起&#xff0c;依据《互联网信息服务管理办法》(国务院令第292号)规定&#xff0c;电信主管部门对从事互联网信息服务的网站开展备案核准工作(即ICP备案)。经过20多年的持续优化完善&#xff0c;已形成“电信主管部门-网络接入服务提供者-互联网信息…

八. 实战:CUDA-BEVFusion部署分析-spconv原理

目录 前言0. 简述1. 举例分析spconv的计算流程2. 导出带有spconv网络的onnx需要考虑的事情总结下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习下课程第八章——实战&a…

JavaWeb- Tomcat

一、概念 老规矩&#xff0c;先看维基百科&#xff1a;Apache Tomcat (called "Tomcat" for short) is a free and open-source implementation of the Jakarta Servlet, Jakarta Expression Language, and WebSocket technologies.[2] It provides a "pure Ja…

什么是广告联盟?国内哪些广告联盟?广告联盟如何赚取收益?

开发者想要对接广告联盟获得广告变现收益&#xff0c;就要了解广告联盟的优势&#xff0c;以及广告联盟是如何获取收益的。 一、什么是广告联盟&#xff1f; 广告联盟是一种在线广告服务模式&#xff0c;将广告主和流量主联系在一起。通过广告联盟平台的技术服务&#xff0c;…

自动驾驶HWP的功能定义

一、功能定义 高速路自动驾驶功能HWP是指在一般畅通高速公路或城市快速路上驾驶员可以放开双手双脚&#xff0c;同时注意力可在较长时间内从驾驶环境中转移&#xff0c;做一些诸如看手机、接电话、看风景等活动&#xff0c;该系统最低工作速度为60kph。 如上两种不同环境和速度…

c++基础 易道云笔记

c基础语法 编程快捷操作使用方法 反汇编&#xff1a; 先设置一个断点&#xff0c;调试后&#xff0c;在调试菜单中选择窗口&#xff0c;选择反汇编 **单词替换&#xff1a;**先按下ctrlf查找&#xff0c;再替换 基础知识辨析 1.数组指针 int &#xff08;*ptest)[5] {} //该…

sqlilabs第四十九五十关

Less-49(GET - Error based - String Bind - ORDER BY CLAUSE) 手工注入 无回显(还是单引号闭合)&#xff0c;只能使用延时注入 自动脚本 和上一关一样 Less-50(GET - Error based - ORDER BY CLAUSE -numeric- Stacked injection) 手工注入 这里需要使用堆叠注入的思路 自…