V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
lxy42
V2EX  ›  Python

最近抽空造了一个数据校验的轮子 Python -validator

  •  
  •   lxy42 · 2018-07-30 14:32:28 +08:00 · 3892 次点击
    这是一个创建于 2316 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近抽空造了一个数据校验的轮子 python-validator

    在开发 web 应用时,经常需要校验前端传入的数据。如果使用 Django,那么可以使用自带的 forms 进行数据校验。

    python-validator 的灵感也是来源于 Django 的 forms (类似 ORM 的方式定义数据结构),不过 python-validator 更加纯粹,只是数据校验,另外也支持使用 dict 定义数据结构,相比于使用类更加方便。

    欢迎大家提建议。觉得不错麻烦给个 star 吧!

    下面是简单的介绍:

    python-validator 是一个类似于 Django ORM 的数据校验库,适用与任何需要进行数据校验的应用,比较常见的是 Web 后端校验前端的输入数据。

    特性

    • 支持 python2 和 python3。

    • 使用类描述数据结构,数据字段一目了然。另外也支持使用字典定义数据结构。

    • 可以自动生成用于测试的 mocking data。

    • 可以打印出清晰的数据结构。

    • 易于扩展。

    依赖

    • six

    • IPy

    • pytz[可选,DatetimeFieldtzinfo 参数需要一个 tzinfo 对象]

    安装

    pip install python-validator

    快速入门

    假设现在正在开发一个上传用户信息的接口 POST /api/user/,用户信息如下:

    • name

      string,必选。

    • age

      integer,可选,默认 20。

    • sex

      string, 'f'表示女, 'm'表示男。可选, 默认 None。

    原始的、枯燥无味的、重复性劳动的数据校验代码可能是下面这样:

    def user(request):
        # data = json.loads(request.body)
        data = {
            'age': '24f',
            'sex': 'f'
        }
        name = data.get('name')
        age = data.get('age', 20)
        sex = dage.get('sex')
    
        if name is None or len(name) == 0:
            return Response('必须提供 name', status=400)
    
        try:
            age = int(age)
        except ValueError as e:
            return Response('age 格式错误', status=400)
    
        if sex is not None and sex not in ('f', 'm'):
            return Response('sex 格式错误', status=400)
    
        user_info = {
            'name': name,
            'age': age,
            'sex': sex,
        }
        ...
    

    上面这段代码总的来说有几个问题:

    • 枯燥无味和重复性代码,不断的取出数据,检查字段是否缺失,类型是否合法等等。

    • 从数据校验的代码无法轻易看出用户信息的数据结构,即字段是什么类型的,是否可选,默认值是什么。

    使用 python-validator 校验数据

    首先定义一个 UserInfoValidator 类

    # validators.py
    from validator import Validator, StringField, IntegerField, EnumField
    
    class UserInfoValidator(Validator):
        name = StringField(max_length=50, required=True)
        age = IntegerField(min_value=1, max_value=120, default=20)
        sex = EnumField(choices=['f', 'm'])
    

    接下来使用 UserInfoValidator 进行数据校验,

    from .validators import UserInfoValidator
    
    def user(request):
        # data = json.loads(request.body)
        data = {
            'age': '24',
            'sex': 'f'
        }
        v = UserInfoValidator(data)
        if not v.is_valid():
            return Response({'msg': v.str_errors, 'code': 400}, status=400)
    
        user_info = v.validated_data
        ...
    

    v.str_errors 是一个字段名 - 错误信息的 dict,例如:

    {'age': 'got a wrong type: str, expect integer', 'name': 'Field is required'}
    

    错误信息解释:

    • age 等于 "24",不是合法的 int 类型。

    • name 是必须提供的,且没有指定默认值。

    v.validated_data 是校验后合法的数据,例如:

    {'age': 24, 'name': u'Michael', 'sex': 'f'}
    

    下面是一些错误数据的例子:

    data:  {'age': 24, 'name': 'abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc', 'sex': 'f'}
    is_valid: False
    errors: {'name': 'string is too long, max-lenght is 50'}
    validated_data: None
    
    data:  {'age': 24, 'name': 'Michael', 'sex': 'c'}
    is_valid: False
    errors: {'sex': "'c' not in the choices"}
    validated_data: None
    
    14 条回复    2018-08-03 18:25:22 +08:00
    onlywinnie
        1
    onlywinnie  
       2018-07-30 15:08:20 +08:00
    先 start 为敬🌚
    Trim21
        2
    Trim21  
       2018-07-30 15:16:00 +08:00 via Android
    好东西…
    能否允许尝试把“ 24 ”被转换为 24 之后再进行校验?
    放在 query string 里面的整数一般取出来还是字符串类型…
    Trim21
        3
    Trim21  
       2018-07-30 15:16:32 +08:00 via Android
    @Trim21 把“ 24 ”转换为 24
    Trim21
        4
    Trim21  
       2018-07-30 15:19:58 +08:00 via Android
    @Trim21 原来文档里有…无视我😂
    lxy42
        5
    lxy42  
    OP
       2018-07-30 15:30:19 +08:00
    @Trim21 #2 我当初考虑到了这个情况,将 strict 设为 False 就会尝试进行类型转换了。

    同理,StringField 也可以允许非字符串类型的数据,只要可以转换成字符串。
    omph
        6
    omph  
       2018-07-30 16:00:52 +08:00
    star 了,挺有用的工具
    E1n
        7
    E1n  
       2018-07-30 16:11:29 +08:00
    GJ!
    d18
        8
    d18  
       2018-07-30 16:21:45 +08:00   ❤️ 1
    marshmallow ?
    lxy42
        9
    lxy42  
    OP
       2018-07-30 16:42:27 +08:00
    @d18 #8 刚看了一下这个库,是有点类似。
    ebingtel
        10
    ebingtel  
       2018-07-31 09:09:36 +08:00
    看着比 wtforms 舒服
    lxy42
        11
    lxy42  
    OP
       2018-07-31 10:57:04 +08:00
    @ebingtel #10 如果在使用过程中遇到问题欢迎提交 issue。
    conn4575
        12
    conn4575  
       2018-08-02 08:59:17 +08:00 via Android
    挺好的,不过 django-restful 里面有个类似的,可以参考一下,我之前用过 schema 来做验证,不过对于嵌套的 json 比较捉急,不知道你这个可以吗?
    lxy42
        13
    lxy42  
    OP
       2018-08-02 09:15:08 +08:00
    @conn4575 #12 你说的是 Django-restfulframework 的 serializer 吗? serializer 其实和 Django 自带的 forms 表单验证是类似的,多了反序列化功能。

    schema 我也看了一下,感觉描述数据约束的方式不够优雅和直观。

    python-validator 支持嵌套的。使用 DictField 就行了,文档在这里 https://ausaki.github.io/python-validator/fields/#dictfield。
    gjquoiai
        14
    gjquoiai  
       2018-08-03 18:25:22 +08:00
    _(:з)∠)_ 用惯了 jsonschema
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2714 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 07:39 · PVG 15:39 · LAX 23:39 · JFK 02:39
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.