Flask 构建 Web API Wheel(八)—— WTForms 校验

Updated on in Python with 797 views

对请求数据的校验,选择使用 WTForms 进行校验,原生 WTForms 是对表单数据进行校验,而 API 请求是需要对请求体的参数进行校验,所以我们需要对他进行覆写。

覆写 WTForms

原生的 Form 输出异常信息会以列表的形式,因为异常可能会有多个,在此我们将其拼接成一句 msg,这样会更便于前端展示。返回校验后的数据可以返回 dict 和 namedtuple 类型,也可以通过 get_data() 获取某一个参数。

# patch/validator.py
class Form(_Form):
    def __init__(self):
        data = request.get_json(silent=True)
        args = request.args.to_dict()
        super(Form, self).__init__(data=data, **args)

    def validate_for_api(self):
        """
        处理异常 拼接异常信息
        """
        valid = super(Form, self).validate()
        if not valid:
            msg = ''
            for index, item in enumerate(self.errors.values()):
                msg += ';'.join(item)
                if index != len(self.errors.values()) - 1:
                    msg += ';'
            raise ParameterError(msg=msg)
        return self

    def get_data(self, *args):
        data_list = []
        for arg in args:
            data_list.append(getattr(self._data, arg, None))
        return data_list[0] if len(data_list) == 1 else tuple(data_list)

    @property
    def _data(self):
        self.validate_for_api()
        key_list, value_list = [], []
        for key, value in self._fields.items():
            if value.data is not None:
                key_list.append(key)
                value_list.append(value.data)
        NamedTuple = namedtuple('NamedTuple', [key for key in key_list])
        return NamedTuple(*value_list)

    @property
    def dt_data(self):
        """
        返回dict类型
        """
        return self._data._asdict()

    @property
    def nt_data(self):
        """
        返回namedtuple类型
        """
        return self._data

使用WTForms

在查看了 WTForms 的文档后,我发现他的 IntegerFieldDataRequired 尽然不支持数字0的,我不得不对它进行覆写,因为在我的理解里 0 也是 IntegerField。

class DataRequired(_DataRequired):
    def __call__(self, form, field):
        if field.type == 'IntegerField' and field.data == 0:
            return

        if not field.data or isinstance(field.data, string_types) and not field.data.strip():
            if self.message is None:
                message = field.gettext('This field is required.')
            else:
                message = self.message

            field.errors[:] = []
            raise StopValidation(message)

使用校验时尽量参考官方文档给出的类型和属性。

class UpdateUserValidator(Form):
    nickname = StringField('昵称')
    avatar = StringField('头像')
    gender = SelectField('性别', choices=GenderEnum.choices(), validators=[Optional()])
    age = IntegerField('年龄', validators=[Optional(), NumberRange(min=1, max=500, message='年龄超出范围')])
    mobile = StringField('手机', validators=[
        Optional(),
        length(min=11, max=11, message='手机号长度为11位'),
        Regexp(r'^1[3-9]\d{9}$', message='手机号不合法'),
    ])

WTForms 所支持的字段

字段类型 说明
StringField 文本字段
TextAreaField 多行文本字段
PasswordField 密码文本字段
HiddenField 隐藏文本字段
DateField 文本字段, 值为datetime.date格式
DateTimeField 文本字段, 值为datetime.datetime格式
IntegerField 文本字段, 值为整数
DecimalField 文本字段, 值为decimal.Decimal
FloatField 文本字段, 值为浮点数
BooleanField 复选框, 值为True 和 False
RadioField 一组单选框
SelectField 下拉列表
SelectMultipleField 下拉列表, 可选择多个值
FileField 文件上传字段
SubmitField 表单提交按钮
FormFiled 把表单作为字段嵌入另一个表单
FieldList 子组指定类型的字段

WTForms支持的表单的验证函数

验证函数 说明
Email 验证是电子邮件地址
EqualTo 比较两个字段的值; 常用于要求输入两次密钥进行确认的情况
IPAddress 验证IP网络地址
Length 验证输入字符串的长度
NumberRange 验证输入的值在数字范围内
Optional 无输入值时跳过其它验证函数
DataRequired 确保字段中有数据
Regexp 使用正则表达式验证输入值
URL 验证url
AnyOf 确保输入值在可选值列表中
NoneOf 确保输入值不在可选列表中

标题:Flask 构建 Web API Wheel(八)—— WTForms 校验
作者:Jeffrey

Responses
取消