wtforms 的 StringField 里设置 default 无效

在项目里使用 wtforms,设置了一个带默认值的 StringField,但提交表单如果没有这项或值是空的,则返回为空,并不会使用 default 里的值

例如定义如下表单

def ListForm(Form):
    status = StringField("status", validators=[Optional()], default="all")

提交一个空请求,返回的 status 是空而不是 all。去跟代码并看了下 GitHub 上的 issue 讨论,按 wtforms 官方的说法,这是故意设计成这样的,参考如下内容

大意是,如果用户设置了一个 Field,那么他就应该有值,不然我们就强行设置为空

但是这个逻辑狗屁不通,因为除了 StringField 其他的类型域就没这个问题,比如 IntegerField 就是可以这么用而且能返回正确的 default

def ListForm(Form):
    status = StringField("status", validators=[Optional()], default="all")
    x = IntegerField("x", validators=[Optional()], default=9)

没办法自己新增了一个 StringFieldWithDefault 的类来解决这个问题(被覆盖的代码可以看上面 GitHub Issue 里的讨论)

class StringFieldWithDefault(StringField):
    def process_formdata(self, valuelist):
        if valuelist:
            self.data = valuelist[0]
        else:
            self.data = self.object_data

而且 wtforms 的数据校验也是谜一般的逻辑,比如设置如下

def ListForm(Form):
    status = StringField("status", validators=[Optional(), AnyOf(["all", "visible"])], default="all")
    x = IntegerField("x", validators=[DataRequired()])

这里会出现两个问题

一是 Optional 如果发现没有值或空,是能通过这个校验的,这里逻辑也没错,但是特喵的 Optional 如果遇到值为空的时候,会把之前的所有错误都清空,并且停止检查后面的 validator。这个迷一般的逻辑会导致如果传的空字段,即没有按正常人预期的拿到 default,也没有去执行 AnyOf 的校验,而是直接通过了。这个官方无解,也不打算修,只说在 3.x 的时候考虑加配置参数,不过看进度上一个版本已经是 2015 年发布的 2.1,而且 GitHub 仓库并不活跃,那么只能呵呵呵呵,然后换自己的 StringFieldWithDefault 吧

二是 DataRequired 不是检查 if field.name in formdata,而是检查 if field.data,这就意味着,如果传了一个 0 的整型参数给 IntegerField,是通不过 DataRequired 的验证的,同理还有空字符串。这个可以改用 InputRequired 来解决,还算是官方给了条活路