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

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

一、前置说明


1、本节目标

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


2、相关回顾


二、操作步骤


1、项目目录


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


2、依赖包安装及说明

pip install schema


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


3、代码实现

pyparamvalidate/core/validator.py

...
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 不能为空,且必须是字符串
                'username': schema.And(str, lambda s: len(s.strip()) > 0, error='Username cannot be empty or contain only spaces'),
                # 使用正则判断 phone_number 格式是否正确
                'phone_number': schema.Regex(r'^\d{11}$', error='Invalid phone number format. It should be a 10-digit number.'),
                # email 允许为空,如果值不为空,则使用 validate_email 方法进行格式校验
                'email': schema.And(schema.Or(str, None), lambda s: validate_email(s) if s is not None else True, error='Invalid email format'),
                # age 必须是整形, 且必须大于0小于120
                'age': schema.And(int, lambda n: 0 <= n <= 120, error='Age must be an integer between 0 and 120'),
                # gender 必须是字符串,且必须为 'male', 'female', ''
                'gender': schema.And(str, lambda s: s.lower() in ['male', 'female'], error='Invalid gender'),
                # family_members,必须为列表,并使用 capitalize 方法对家庭成员名称首字母大写
                'family_members': schema.And(schema.Use(list), [schema.Use(capitalize)]),
                # others 是一个嵌套字典
                'others': {
                    # address 不能为空,且必须是字符串
                    'address': schema.And(str, lambda s: s.strip(), error='Address must be a non-empty string'),
                    # blog 允许为空,如果不为空,则使用正则校验规则
                    '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 允许为空和字符串
                    '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'
                }
            }
            # 使用 schema_validate 进行校验
            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))
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 的大部分特性,可用于参考。
  • schema 官方文档,请访问:https://github.com/keleshev/schema


2、下节准备

  • 至此,pyparamvalidate 的核心部分已基本完成。
  • 下一步,将本项目 pyparamvalidate ,发布至 pypi

点击进入《Python装饰器从入门到进阶》总目录

目录
相关文章
|
8月前
|
前端开发
Antd中Table列表行默认包含修改及删除功能的封装
Antd中Table列表行默认包含修改及删除功能的封装
192 0
|
2月前
|
XML 数据库 数据格式
数据库 校验名称唯一性,用于新增和修改功能
数据库 校验名称唯一性,用于新增和修改功能
60 8
|
8月前
|
SQL API 数据库
为API设置默认排序规则结果数据的正确性
Dataphin数据服务支持API调用时通过OrderByList自定义排序,确保数据返回符合业务需求。默认排序在API设计时至关重要,因为它影响用户体验、数据一致性及查询正确性。新版本 Dataphin 提供了排序优先级设置,允许在SQL脚本或OrderByList中指定排序,以适应不同场景。
114 0
|
4月前
|
API PHP 数据库
Laravel框架下通过DB获取数据并转为数组的方法
通过上述方法,Laravel为开发者提供了一套灵活而强大的工具,用于从数据库中检索数据并将其转换为数组。无论是使用DB Facade直接执行查询,还是利用模型的方法,Laravel都能够简化这一过程,使得代码既简洁又富有表现力。在实际开发中,选择最适合你需求的方法可以有效提高开发效率和应用性能。
126 0
|
7月前
|
存储 SQL 数据处理
Django ORM实战:模型字段与元选项配置,以及链式过滤与QF查询详解
Django ORM实战:模型字段与元选项配置,以及链式过滤与QF查询详解
|
7月前
|
JSON 数据格式 微服务
.NET下 支持大小写不敏感的JSON Schema验证方法
有很多应用程序在验证JSON数据的时候用到了JSON Schema。 在微服务架构下,有时候各个微服务由于各种历史原因,它们所生成的数据对JSON Object属性名的大小写规则可能并不统一,它们需要消费的JSON数据的属性名可能需要大小写无关。 遗憾的是,目前的JSON Schema没有这方面的标准,标准中都是大小写敏感的。在类似上述情况下,这给使用JSON Schema进行数据验证造成了困难。
|
8月前
|
数据格式 Python
添加 自定义校验方法,让用户自定义校验规则
添加 自定义校验方法,让用户自定义校验规则
88 0
【Django学习】(九)自定义校验器_单字段_多字段校验_模型序列化器类
【Django学习】(九)自定义校验器_单字段_多字段校验_模型序列化器类
|
存储 数据库
laravel-admin 查询过滤时间戳(数据库使用int类型)不起作用案例复现及解决办法
laravel-admin 查询过滤时间戳(数据库使用int类型)不起作用案例复现及解决办法
289 0
laravel-admin 查询过滤时间戳(数据库使用int类型)不起作用案例复现及解决办法
|
存储 NoSQL Java
数据类型-set 实现权限校验| 学习笔记
快速学习数据类型-set 实现权限校验
数据类型-set 实现权限校验| 学习笔记