我目前正在 Pyramid 中的某个项目上工作,并且对wtforms SelectField有问题。

我有3个SelectField字段:

  • car_make(例如“audi”)
  • car_model(例如“audi 80”)
  • car_version(例如“AUDI 80 B4”)。

  • 我可以在 View 中加载的car_make选项。剩下的选择域(Car_Model,Car_Version)的选择我将通过Ajax/JavaScript加载客户端(我可以在选择Car_Make等时选择Car_Model)。

    问题是,当我提交表单时,car_model和car_version会引发“无效选择”,因为(在SelectField.pre_validation行431中)self.choices为空。

    我该如何解决这个问题?

    最佳答案

    您要执行的操作是让WTForms处理“级联选择”(一种选择的有效字段由另一种字段的值确定)。使用内置字段确实不是一个好方法。

    WTForms中的SelectField不能为您提供一个选项,例如“不验证所提供的选择是否有效”。您必须提供选择,以便字段验证选择。

    shown in the docs一样,虽然通常可以用静态的选项列表填充options字段。

    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 View 中。因此,这就是您需要做的:您需要填写 View 中的选择。这是解决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...
    

    您可能需要稍微调整一下才能使其完全按照您想要的方式工作,但我还没有实际测试过这种方法是否可行。

    10-01 16:03