zoukankan      html  css  js  c++  java
  • 简洁的 Python Schema

    Python Schema使用说明

    项目地址:GitHub

    1. Schema是什么?

    不管我们做什么应用,只要和用户输入打交道,就有一个原则--永远不要相信用户的输入数据。意味着我们要对用户输入进行严格的验证,web开发时一般输入数据都以JSON形式发送到后端API,API要对输入数据做验证。一般我都是加很多判断,各种if,导致代码很丑陋,能不能有一种方式比较优雅的验证用户数据呢?Schema就派上用场了。

    Schema非常简单,也就几百行的代码,最核心的类就一个:Schema。

    2. 安装

    pip install schema
    

    1. 给Schema类传入类型(int、str、float等)

    例如:

    from schema import Schema
    
    Schema(int).validate(10)
    # 10
    Schema(int).validate('10')
    # SchemaUnexpectedTypeError: '10' should be instance of 'int'
    

    可见Schema会去验证validate方法传入的对象是不是所指定的类型,是则返回传入的数据,否则抛出一个SchemaError的异常(SchemaUnexpectedTypeError是SchemaError的子类)。

    2. 给Schema类传入可调用的对象(函数、带__call__的类等)

    例如:

    Schema(lambda x: 0<x<10).validate(5)
    # 5
    Schema(lambda x: 0<x<10).validate(57)
    # SchemaError: <lambda>(57) should evaluate to True
    

    自定义函数:

    def secre_validate(password):
        password_list = ["123456", "666666", "888888", "abcdef", "aaaaaa", "112233"]
        if password in password_list:
            return False
        return True
    
    Schema(secre_validate,  error="字符串已经存在").validate("123456")
    
    # schema.SchemaError: 字符串已经存在
    
    

    可见Schema会把validate方法传入的值传入到对应的函数里面作为参数,如果函数返回值为True则返回输入数据,否则抛出异常。

    3. 给Schema类传入带有validate方法的对象

    Schema也内置了一些类(Use、And、Or等等),这些类的实例都带有validate方法,亦可作为Schema的参数传入,例如:

    from schema import Schema, And
    
    # And代表两个条件必须同时满足
    Schema(And(str, lambda s: len(s) > 2)).validate('abcd')
    # 'abcd'
    

    4. 给Schema类传入容器对象(list、tuple、set等)

    例如:

    Schema([int, float]).validate([1, 2, 3, 4.0])
    # [1, 2, 3, 4.0]
    
    

    相当于,对于[1, 2, 3, 4.0]当中的任何一个元素,必须是int或者float才行(注意是or的关系)

    5. 给Schema传入一个字典对象(大部分使用Schema的场景都是传入字典对象,这个很重要)

    Schema({'name': str, 'age': int}).validate({'name': 'foobar', 'age': 18})
    # {'age': 18, 'name': 'foobar'}
    Schema({'name': str, 'age': int}).validate({'name': 'foobar'})
    # SchemaMissingKeyError: Missing keys: 'age'
    

    首先,明确两个概念,Schema类传入的字典,称之为模式字典,valdiate方法传入的字典称之为数据字典。

    首先,Schema会判断, 模式字典和数据字典的key是否完全一样,不一样的话直接抛出异常。如果一样,就去拿数据字典的value去验证模式字典相应的value,如果数据字典的全部value都可以验证通过的话才返回数据,否则抛出异常,是不是感觉这种验证顿时感觉清爽了呢?

    6. faqs

    6.1 Schema传入字典很好用,但是我有的数据是可选的,也就是说有的key可以不提供怎么办?

    from schema import Optional, Schema
    
    
    Schema({'name': str, Optional('age'): int}).validate({'name': 'foobar'})
    # {'name': 'foobar'}
    Schema({'name': str, Optional('age', default=18): int}).validate({'name': 'foobar'})
    # {'age': 18, 'name': 'foobar'}
    

    6.2 禁止传入某个key:Forbidden

    Forbidden可以将某个key禁止:

    from schema import Schema, Forbidden
    
    Schema({Forbidden('name'): str, 'age': int}).validate({"age": 15})                            # {"age": 15}
    Schema({Forbidden('name'): str, 'age': int}).validate({"name": "laozhang", "age": 15})        # schema.SchemaForbiddenKeyError
    Schema({Forbidden('name'): str, 'age': int}).validate({"name": 10, "age": 15})                # schema.SchemaWrongKeyError
    
    

    值得注意的是,与禁用秘钥配对的值将决定了它是否会被拒绝:

    from schema import Schema, Forbidden
    
    Schema({Forbidden('name'): int, 'name': str}).validate({'name': 'laozhang'})                  # {'name': 'laozhang'}
    Schema({Forbidden('name'): str, 'name': str}).validate({'name': 'laozhang'})                  # schema.SchemaForbiddenKeyError
    

    另外,Forbidden的优先级要比Optional要高:

    from schema import Schema, Forbidden, Optional
    
    Schema({Forbidden('name'): str, Optional('name'): str}).validate({"name": "laozhang"})        # schema.SchemaForb
    

    6.3 我想让Schema只验证传入字典中的一部分数据,可以有多余的key但是不要抱错,怎么做?

    Schema({'name': str, 'age': int}, ignore_extra_keys=True).validate({'name': 'foobar', 'age': 100, 'sex': 'male'})
    # {'age': 100, 'name': 'foobar'}
    

    6.4 Schema抛出的异常信息不是很友好,我想自定义错误信息,怎么办?

    Schema自带的类(Use、And、Or、Regex、Schema等)都有一个参数error,可以自定义错误信息

    Schema({'name': str, 'age': Use(int, error='年龄必须是整数')}).validate({'name': 'foobar', 'age': 'abc'})
    # SchemaError: 年龄必须是整数
    

    6.5 Use

    Use在验证的时候,会自动帮你转换它的值

    from schema import Schema, Use
    
    print Schema(Use(int)).validate(10)                 # 10-->int类型
    print Schema(Use(int)).validate('10')               # 10-->int类型
    print Schema(Use(int)).validate('xiaoming')         # schema.SchemaError
    

    6.6 Const

    我们知道Use在验证的时候,会自动帮你转换它的值。Const可以保持原始数据不变:

    from schema import Schema, Use, Const
    
    print Schema(Const(Use(int))).validate('10')        # 10-->str类型
    

    7. 一个稍微复杂的例子:

    from schema import Schema, And, Optional, SchemaError, Regex
    
    
    def name_check(name):
        password_list = ["root", "admin", "888888", "baba", "aaaaaa", "112233"]
        if name in password_list:
            return False
        return True
    
    
    schema = {
        "id": And(int, lambda x: 100 <= x, error="id必须是整数,大于等于100"),
        "name": And(str, name_check, error="name已经存在"),
        "price": And(float, lambda x: 111 > x > 0, error="price必须是大于0小于111的小数"),
        "data": {
            "password": And(Regex("[a-z0-9A-Z]{8,20}"), error="密码为数字、字母,长度8-20"),
            "email": And(Regex("^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$"), error="email格式错误")
        },
        Optional("info", default="这个key可以不提供,我设置了这一段默认值。"): str,
    }
    
    data = {
        "id": 111,
        "name": "jarvis",
        "price": 9.5,
        "data": {
            "password": "222agfwetAAA",
            "email": "www.qq.afsdf@email.cn",
        }
    }
    
    try:
        a = Schema(schema).validate(data)
    except SchemaError as e:
        print(e)
    else:
        print("验证成功!
    {}".format(a))
    
    
    # 验证成功!
    # {'id': 111, 'name': 'jarvis', 'price': 9.5, 'data': {'password': '222agfwetAAA', 'email': 'www.qq.afsdf@email.cn'}, 'info': '这个key可以不提供,我设置了这一段默认值。'}
    

    参考文章:https://segmentfault.com/a/1190000011777230

  • 相关阅读:
    iOS 内购讲解
    实现抓图的工具2
    实现抓图的工具
    关于胖客户端
    实时进销存
    DataGridView隔行显示不同的颜色
    C#数据库绑定
    VS2012中数据库架构的比较
    delphi使用outputdebugstring调试程序和写系统日志
    drupal7安装中文错误
  • 原文地址:https://www.cnblogs.com/ChangAn223/p/11239537.html
Copyright © 2011-2022 走看看