Flask WTForm __init__中设置SelectField choices不生效如何解决
问题原因
你当前写法不生效的核心原因有两个:
- 重写表单类
__init__方法时,没有先调用父类FlaskForm的构造方法。父类构造逻辑会完成字段和表单实例的绑定、初始值加载等操作,跳过这一步直接访问self.dtype,操作的是类层面的未绑定字段,后续实例初始化流程会把你设置的choices覆盖掉。 - 自定义的
__init__只接收**kwargs参数,漏掉了位置参数*args,会导致表单提交时的请求数据、预填充对象参数无法正常传递,除了选项加载失败,还会出现表单校验、数据回填异常的问题。
正确实现方案
方案1:重写__init__方法(逻辑可控,适合需要额外初始化逻辑的场景)
先调用父类构造方法完成基础初始化,再动态加载选项赋值即可:
class DisForm(FlaskForm): dtype = SelectField("dtype", choices=[]) def __init__(self, *args, **kwargs): # 必须优先调用父类构造,完成表单实例的基础初始化 super().__init__(*args, **kwargs) # 初始化完成后再动态加载选项赋值 self.dtype.choices = [(t.name, t.title) for t in DTypes.query.all()]
方案2:给choices传入可调用对象(写法更简洁,无需重写构造方法)
WTForms原生支持choices参数接收无参可调用对象,字段渲染时会自动调用该对象获取最新选项,连__init__方法都不需要重写:
def load_dtype_choices(): # 每次渲染字段时都会执行查询,自动拿到最新的数据库选项 return [(t.name, t.title) for t in DTypes.query.all()] class DisForm(FlaskForm): # 注意这里传函数引用,不要加括号调用 dtype = SelectField("dtype", choices=load_dtype_choices)
注意事项
- 如果选择重写
__init__的方案,super().__init__()的调用顺序不能乱,必须放在所有自定义字段操作之前。 - 传可调用对象的方案更适合选项会频繁变动的场景,不需要重启应用就能拿到新增的选项数据。
内容的提问来源于stack exchange,提问作者Ahmad
相关产品推荐
相关产品推荐

