在跨 FormFields 的不同 FieldLists 验证表单时如何处理错误消息?

How to handle error messages when validating a form across different FieldLists of FormFields?

我有一个包含(在其他字段中)2 个字段列表的表单。每个 FieldList 都是 FormFields 的列表。在每个 FieldList 中,表单都代表项目。一个实地列表用于已取消预算的项目,另一个列表用于将成为该预算接受者的项目。这是一种动态形式;可以有任意数量的解除分配或分配。整个表单中会有很多验证,但我无法工作的一个简单案例是确保没有项目在表单中出现两次(也就是说,在两个列表中,项目 ID 必须是唯一的)。我可以对此进行测试并识别这种情况何时发生,但我无法将错误消息与有问题的项目 ID 相关联。

我的表格 类 的简化版本是:

class AllocationForm(Form):
    project_id(StringField, label='Project ID')
    ... details about amount being allocated here

class DeallocationForm(Form):
    project_id(StringField, label='Project ID')
    ...details about amount being deallocated here

class RequestForm(FlaskForm):

    def validate(self):

        project_id_seen = {}
        project_list = [a for a in self.allocations] + [d for d in self.deallocations]

        for formfield in project_list:
            if project_id_seen.get(formfield.data.get('project_id')) is None:
                project_id_seen[formfield.data.get('project_id')] = 1
            else:
                # *** Here's where I'm having an issue ***
                somethingsomethingsomething['project_id'] = 'A project ID cannot appear more than once in a request'
                return False

        # other form validations happen here
        if not FlaskForm.validate(self):
            return False

    allocations = FieldList(FormField(AllocationForm))
    deallocations = FieldList(FormField(DeallocationForm))

如您所见,我重写了 RequestFormvalidate 方法。我在 else 子句中 return False 的地方,我想将一条错误消息与相关项目 ID 相关联,以便我可以在重新呈现表单时显示它,但我难以访问子表单中的特定 project_id 字段。有什么想法吗?

编辑:我想到另一种方法可能是将验证器放在 AllocationFormDeallocationForm 类 中的项目 ID 字段上,而不是 RequestForm。这是一种更可行的方法吗?将错误消息与字段相关联会更容易,但是 AllocationForm 中的验证器如何访问 DeallocationForm 中的项目 ID,反之亦然?

你使用 validate 的方法对我来说很有意义,我认为你接近实现你的目标。
为了能够将错误消息分配给一个字段,您可以临时存储必要的字段,然后在需要时向相应字段的错误消息元组中添加一个条目。

from collections import defaultdict

class AllocationForm(Form):
    project_id = StringField('Project ID')
    # ...

class DeallocationForm(Form):
    project_id = StringField('Project ID')
    # ...

class RequestForm(FlaskForm):
    allocations = FieldList(FormField(AllocationForm))
    deallocations = FieldList(FormField(DeallocationForm))

    def validate(self):
        data_fields = defaultdict(list)
        for field in (self.allocations, self.deallocations):
            for form in field:
                if form.project_id.data:
                    data_fields[form.project_id.data].append(form.project_id)

        success = True
        for data,fields in data_fields.items():
            if len(fields) > 1:
                for field in fields:
                    msg = 'A project ID cannot appear more than once in a request'
                    field.errors += (msg,)
                success = False

        return success and super().validate()