您要求做的是让 WTForms 处理“级联选择”(让一个选择的有效字段由另一个字段的值确定)。确实没有使用内置字段的好方法。
WTForms 中的 SelectField 没有为您提供“不验证提供的选择是否有效”的选项。您必须提供选择才能使该字段验证选择。
作为shown in the docs,虽然您通常可以使用静态选项列表填充选项字段...
class PastebinEntry(Form):
language = SelectField(u'Programming Language', choices=[('cpp', 'C++'), ('py', 'Python'), ('text', 'Plain Text')])
...但是,由于您是动态提出选项,因此您需要在实例化表单后设置choices 属性。
def edit_user(request, id):
user = User.query.get(id)
form = UserDetails(request.POST, obj=user)
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
在上面的示例中,“group_id”的选项会动态填充到您的 Pyramid 视图中。所以,这就是你需要做的:你需要在你的视图中填写选项。这就是你如何解决car_make 的问题(尽管我认为在你的问题中你说car_make 没问题)。
但是,您遇到的问题是无法确定 car_model 的有效选择,因为它们取决于 car_make 已经被解析和验证。 WTForms 并不能很好地处理这个问题(至少对于 SelectFields 而言),因为它假定所有字段都应该一次验证。换句话说,为了生成car_model 的有效选择列表,您首先需要验证car_make 的值,鉴于 SelectField 的工作原理,这并不容易。
我认为这样做的最佳方法是创建一个扩展 SelectField 类型的新字段类型,但删除了验证:
class NonValidatingSelectField(SelectField):
def pre_validate(self, form):
pass
这种新类型会覆盖 pre_validate,它通常会进行检查以确定选择是否有效。
如果您将它用于car_model,您将不再有错误。但是,这现在意味着您的字段实际上并没有被验证!要解决此问题,您可以在表单上添加 in-line validator...
class MyForm(Form):
car_make = SelectField(u'Make', choices=[...])
car_model = NonValidatingSelectField(u'Model', choices=[])
def validate_car_model(self, field):
choices = query_for_valid_models(self.car_make.data)
# check that field.data is in choices...
你可能需要稍微调整一下才能让它完全按照你想要的方式工作,我还没有实际测试过它是否有效。