【问题标题】:Django form.clean() run before field validatorsDjango form.clean() 在字段验证器之前运行
【发布时间】:2017-01-22 03:56:09
【问题描述】:

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() 方法在现场验证器之前运行?

【问题讨论】:

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


    【解决方案1】:

    将您的表单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() 方法被调用之后。

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

    【讨论】:

    • 谢谢,结果是一样的。当我为嵌套的内联表单和顶级基本表单尝试此解决方案时,它们都只在我的 clean 函数(现在调用 cleaned_data = super(YourModelForm, self).clean())完成后调用验证器。我将再次仔细查看文档,并检查嵌套表单是否有任何不同。
    • 我已经修改了它(原始问题更新了完整的代码)是最基本的应该可以工作的例子,但它仍然在验证器之前运行clean
    • super 的第一个参数在大多数情况下应该是与第二个参数相同的类。在这种情况下super(KeywordAdminForm, self)
    • 谢谢,我复制错了。它仍然在我的验证码()之前调用 clean;我在 validate_ascii 函数中放置了一个断点,它只有在 clean 函数完成后才会到达那里。
    • docs 的这一部分对您的问题进行了解释。 “模型验证 (Model.full_clean()) 是在表单验证步骤中触发的,在表单的 clean() 方法被调用之后。”。这表明您不能依赖 clean 方法中的任何模型验证
    【解决方案2】:

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

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

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

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

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

    • 但是,如同一章的"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__'
      

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-09
      • 1970-01-01
      • 2011-06-22
      • 1970-01-01
      • 2013-03-05
      • 2018-06-18
      • 2023-03-29
      相关资源
      最近更新 更多