Django form.clean()在字段验证器之前运行

时间:2016-09-14 11:02:19

标签: django forms validation django-forms django-validation

https://docs.djangoproject.com/en/1.10/ref/forms/validation/

run_validators()在表单子类clean()之前运行的状态。

我的模型看起来像:

def validate_ascii(value):
    try:
        value.encode('ascii')
    except UnicodeEncodeError:
        raise ValidationError("Contains non-ascii characters")

class Keyword(models.Model):
    name = models.CharField(max_length=50, unique=True, validators=[validate_ascii])

在我的表单clean()方法

class KeywordAdminForm(ModelForm):
    class Meta:
        model = Keyword

    def clean(self):
        import pdb; pdb.set_trace()
        cleaned_data = super(KeywordAdminForm, self).clean()
        import pdb; pdb.set_trace()
        return super(KeywordAdminForm, self).clean()

之后,运行表单中每个字段的验证器。这会导致问题,因为我的clean方法假定每个字段都先运行validator并崩溃。

为什么我的表单的clean()方法在字段上的验证器之前运行?

2 个答案:

答案 0 :(得分:3)

更改您的表单clean()方法,以便在执行其余验证之前先调用cleaned_data = super(KeywordAdminForm, self).clean()This is how the docs recommend you do it

This section of the docs对您的问题有解释。

  

模型验证(Model.full_clean())是从内部触发的   表单验证步骤,在调用表单的clean()方法之后。

这表明您不能依赖干净方法中的任何模型验证

答案 1 :(得分:0)

根据文档的“ Validation on a ModelForm”段:

  

验证ModelForm涉及两个主要步骤:

     
      
  1. 验证表单
  2.   
  3. 验证模型实例
  4.   

这定义了两层完全不同的验证层,一层在模型层,一层在表单层。

因此,期望这些验证层以某种方式相关显然是错误的。

  • 但是,有一个合理的解决方案,如同一章的"Overriding the Default Fields“段落中所述:

      

    如果您想指定字段的验证者,可以通过定义   声明性地设置字段并设置其验证器参数。

    您的示例可能变成:

    from django.forms import CharField, ModelForm
    from myapp.models import Keyword
    
    class KeywordAdminForm(ModelForm):
        slug = CharField(max_length=50, validators=[validate_ascii])
    
        class Meta:
            model = Keyword
            fields = '__all__'
    

    请记住,但请阅读此示例后面的绿色“注释”,其中指出:

      

    类似地,以声明方式定义的字段不会绘制其属性   如来自相应模型的max_lengthrequired。如果你想   要维持模型中指定的行为,您必须设置   声明表单字段时明确地提供相关参数。

  • 或者,您可以执行以下操作:

    from django.forms import CharField, ModelForm
    from myapp.models import Keyword, validate_ascii
    
    class KeywordAdminForm(ModelForm):
        def clean_slug(self):
            slug = self.cleaned_data.get('slug')
            validate_ascii(slug)
            return slug
    
        def clean(self):
            cleaned_data = super().clean()
            if self.errors:
                return cleaned_data
            ...
            return cleaned_data
    
        class Meta:
            model = Keyword
            fields = '__all__'
    

    上面的代码之所以有效,是因为它可能在ValidationError之前调用的clean_<field>()内引发一个clean()