Jeffrey的博客 Jeffrey的博客

左脑编程,右脑写诗

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

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

对请求数据的校验,选择使用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