我可以依靠Django表单中的字段验证顺序吗?

Ned*_*der 9 django validation

我有一个带有用户名和电子邮件字段的Django表单.我想查看用户尚未使用的电子邮件:

def clean_email(self):
    email = self.cleaned_data["email"]
    if User.objects.filter(email=email).count() != 0:
        raise forms.ValidationError(_("Email not available."))
    return email
Run Code Online (Sandbox Code Playgroud)

这有效,但会引发一些漏报,因为电子邮件可能已经存在于表单中指定用户的数据库中.我想改成这个:

def clean_email(self):
    email = self.cleaned_data["email"]
    username = self.cleaned_data["username"]
    if User.objects.filter(email=email,  username__ne=username).count() != 0:
        raise forms.ValidationError(_("Email not available."))
    return email
Run Code Online (Sandbox Code Playgroud)

Django文档说,一个字段的所有验证都是在进入下一个字段之前完成的.如果在用户名之前清除了电子邮件,那么cleaned_data["username"]将无法使用clean_email.但是文档还不清楚字段清理的顺序.我在表单中的电子邮件之前声明用户名,这是否意味着我可以安全地假设用户名在电子邮件之前被清除了?

我可以阅读代码,但我对Django API的承诺更感兴趣,并且知道即使在未来版本的Django中我也很安全.

Jor*_*ter 8

更新

.keyOrder不再有效.我相信这应该有效:

from collections import OrderedDict


class MyForm(forms.ModelForm):
    …

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        field_order = ['has_custom_name', 'name']
        reordered_fields = OrderedDict()
        for fld in field_order:
            reordered_fields[fld] = self.fields[fld]
        for fld, value in self.fields.items():
            if fld not in reordered_fields:
                reordered_fields[fld] = value
        self.fields = reordered_fields
Run Code Online (Sandbox Code Playgroud)

上一个答案

无论您如何在表单定义中声明它们,都可以改变表单顺序.其中一个是如果你正在使用a ModelForm,在这种情况下,除非你在fields下面声明了两个字段,否则class Meta它们将处于不可预测的顺序.

幸运的是,有一个可靠的解决方案.

您可以通过设置控制表单中的字段顺序self.fields.keyOrder.

以下是您可以使用的示例代码:

class MyForm(forms.ModelForm):
    has_custom_name = forms.BooleanField(label="Should it have a custom name?")
    name = forms.CharField(required=False, label="Custom name")

    class Meta:
        model = Widget
        fields = ['name', 'description', 'stretchiness', 'egginess']

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        ordered_fields = ['has_custom_name', 'name']
        self.fields.keyOrder = ordered_fields + [k for k in self.fields.keys() if k not in ordered_fields]

    def clean_name(self):
        data = self.cleaned_data
        if data.get('has_custom_name') and not data.get('name'):
            raise forms.ValidationError("You must enter a custom name.")
        return data.get('name')
Run Code Online (Sandbox Code Playgroud)

使用keyOrderset,has_custom_name将在验证self.cleaned_data之前进行验证(因此存在)name.


Dan*_*man 7

没有承诺以任何特定顺序处理字段.官方建议,任何依赖于多个字段的验证都应该使用表单的clean()方法,而不是特定于字段的clean_foo()方法.

  • 这是不正确的。对字段的处理顺序有一个承诺。它只是在所有版本中都没有得到很好的保存。也许较新的 Django 版本确实兑现了这一承诺,但我还没有验证这一点。 (2认同)

Wol*_*lph 7

Django文档声称它是按字段定义的顺序.

但我发现它并不总是能够实现这一承诺.资料来源:http://docs.djangoproject.com/en/dev/ref/forms/validation/

这些方法按上面给出的顺序运行,一次一个字段.也就是说,对于表单中的每个字段(按照它们在表单定义中声明的顺序),运行Field.clean()方法(或其覆盖),然后运行clean_().最后,一旦为每个字段运行这两个方法,就会执行Form.clean()方法或其覆盖.

  • 那不是真的 Ned Batchelder,看看这部分:`对于表单中的每个字段(按照它们在表单定义中声明的顺序)` (3认同)