美文网首页
wtForms 使用简介

wtForms 使用简介

作者: 番茄树叶 | 来源:发表于2017-06-19 23:04 被阅读1425次

近期在用aiohttp替换tornado重构一部分api,其中一些接口需要验证比较多的请求参数,之前同事开发时挺赶的,直接在函数里一个个地验证参数,光验证参数得写一百五六十行代码。我重构时,想到用表单验证来解决这个问题,让代码不至于那么臃肿。


官方文档

http://wtforms.readthedocs.io/en/latest/


示例程序

示例程序web部分使用的是aiohttp,当然换成其他的像flask、tornado都行的。

import json
import traceback
from multidict import MultiDict
from aiohttp import web
from wtforms import Form, StringField, validators, ValidationError

class ActionForm(Form):
    action = StringField('action', [validators.Required()])

async def task_handler(request):
    try:
        post_data = await request.text()
        post_data = json.loads(post_data)
        _form = ActionForm(MultiDict(post_data))
        if _form.validate():
            print('>>>> {} validate success'.format(post_data))
        else:
            print('<<<< {}'.format(_form.errors))
            return web.json_response({'message': 'form validate failed'}, status=400)
        return web.json_response({'message': 'task_handler'})
    except:
        traceback.print_exc()
        return web.json_response({'message': 'exception'})

app = web.Application()
app.router.add_post('/task', task_handler)

web.run_app(app, host='127.0.0.1', port=8080)

使用curl测试

curl -l -H "Content-type: application/json" -X POST -d '{"action": "START"}' 127.0.0.1:8080/task
>>> {"message": "task_handler"}

说明

  • 定义了ActionForm,验证一个类型为String的字段action, validators.Required()表示该字段必须要有,若无则无法通过验证,validators.optional()表示该参数可选
  • 若post_data中有其他参数未在ActionForm定义的话,不会影响form的验证,直接忽略
  • 除了StringField外,还有其他如:BooleanField、DateField、IntegerField、PasswordField、FieldList、FormField等等,可以查看官方文档
  • FieldList是一个列表字段,可以指定list中field的类型
  • FormField类似一个字典字段,可以嵌套定义
  • 初始化Form后,调用validate()进行验证,若通过验证则返回True,否则返回False,失败的情况加Form的errors返回所有所有验证失败的字段及失败信息

自定义验证函数 Custom validators

在使用过程中,会有需求对字段进行更细化的验证,比如:希望action字段的字符串值在['START', 'CANCEL']范围内,此时就需要编写自定义的验证函数,对字段进行追加验证。

增加一个validator,用于验证action字段在['START', 'CANCEL']范围内,若不在则抛出ValidationError异常:

def my_action_check(form, field):
    if field.data not in ['START', 'CANCEL']:
        raise ValidationError('action must in [START, CANCEL]')

ActionFormaction字段的validator列表中加入my_action_check,wtForms会按照先后顺序调用所有的validator对字段进行验证(链式调用,某个环节失败则不继续验证)

class ActionForm(Form):
    action = StringField('action', [validators.Required(), my_action_check])
  • 此时调用接口时若传入action参数不为STARTCANCEL,则表单无法通过验证。
  • 验证函数的form字段即为当前的form对象,可用来获取其他字段的值进行联合验证
  • 验证函数的field字段即为当前的字段对象,通过field.data来获取值
In-line Validators (内联?。。不知道怎么翻译)

以上的方式使得验证函数具备一定的通用性,这里还有另一种方式用于验证字段,在Form类中定义validate_fieldname格式的函数,则Form会自动调用来验证对应的字段,函数同样接收两个参数:form, field

class ActionForm(Form):
    action = StringField('action', [validators.Required()])
        
    def validate_action(form, field):
        if field.data not in ['START', 'CANCEL']:
            raise ValidationError('action must in [START, CANCEL]') 

以上两种验证方式效果时一样的


自定义字段 Custom Fields

有的场景下,会post复杂的json数据,wtForms自带的字段会感觉不太够用,此时可以自己定义一个字段,能灵活验证数据。如嫌FieldList或者FormField用起来麻烦,可以自己定义一个ListField、DictField来验证列表和字典字段。

自定义ListField,验证数组

class ListField(Field):

    def process_formdata(self, valuelist):
        try:
            if valuelist[0] and isinstance(valuelist[0], list):
                self.data = valuelist[0]
            else:
                raise ValidationError('list validate error')
        except:
            raise ValidationError('list validate error, exception')

使用curl测试

curl -l -H "Content-type: application/json" -X POST -d '{"action": "START", "users": ["Tracy", "Kobe", "KD"]}' 127.0.0.1:8080/task

>>> test python3 aiotest.py
======== Running on http://127.0.0.1:8080 ========
(Press CTRL+C to quit)
>>>> {'action': 'START', 'users': ['Tracy', 'Kobe', 'KD']} validate success

users字段不传数组或数组为空则form无法通过验证

  • 自定义ListField时,需重写process_formdata方法处理初始化时传入的值,值通过valuelist[0]获取,若验证通过则将值赋给self.data,那么这个字段就有值了,后续的validators就可以对此值进行验证
  • validators.Required()实际做的事情就是判断字段的self.data是否为True

小结

wtForms还有一些其他用法在此就不再介绍了,翻阅文档吧。本文代码均使用python3.5运行。若有问题,欢迎交流。

相关文章

网友评论

      本文标题:wtForms 使用简介

      本文链接:https://www.haomeiwen.com/subject/xzynqxtx.html