将Django模型验证错误转移到表单的惯用法

时间:2011-10-12 07:26:58

标签: python django

请帮助我理解我所做的以下选择是否惯用/好,如果没有,如何改进。

1)表单验证的模型验证 我希望尽可能使用新的model validation覆盖表单验证,因为它似乎是创建数据规则的更干燥和基本的方法。简单日历条目模型的两个示例:

  • “开始必须在结束前”
  • “期间必须小于(结束 - 开始)”

将这些放在模型级别以使它们不必被置于表格中是否惯用/好?如果ModelForm是最佳答案,那么在同一表格中使用多个模型的情况又如何呢?
(编辑:我没有意识到多个ModelForms实际上可以一起使用)

2)将模型验证转移到表单(而不是ModelForm)
(编辑:事实证明,在我的情况下,重新设计模型验证和表单验证之间的管道并不是必需的,下面的解决方案显示了原因)
让我们假设我所拥有的任何模型验证错误都可以直接传输并直接显示给用户(即忽略将模型验证错误转换为用户友好的表单验证错误)。

这是我想出来的一种工作方式(所有在一个地方,没有辅助功能):

view_with_a_form:
...
if form.is_valid():
    instance = ...
    ...
    #save() is overridden to do full_clean with optional exclusions
    try: instance.save()
    except ValidationError e:
        nfe= e.message_dict[NON_FIELD_ERRORS]
        #this is one big question mark:
        instance._errors['__all__'] = ErrorList(nfe)
#this is the other big question mark. seems unnatural
if form.is_valid()
    return response...
... #other code in standard format
#send the user the form as 1)new 2)form errors 3)model errors
return form

如代码所示:
a)这是将模型错误转移到表格的惯用/好方法吗?

b)这是测试新“表单”错误的惯用/好方法吗?

注意:此示例使用非字段错误,但我认为它同样适用于字段错误。

2 个答案:

答案 0 :(得分:5)

[编辑 - 希望这能回答你的意见]

我会简短而直接,但不要不礼貌。)

1)表单验证的模型验证

我想经验法则可能只是规则确实与模型相关联,是的,最好在模型级别进行验证。

对于多模式表单,请查看其他SO问题:Django: multiple models in one template using forms,不要忘记attached link稍微陈旧但仍然与最干燥方式相关的问题它。在这里重新讨论它太长了!

<强> 2)

  • a)不!您应该从将为您执行model validation的ModelForm派生您的表单 - &gt;您不需要自己调用模型验证
  • b)不!如果要验证模型,则不应尝试保存;你应该使用full_clean - &gt;因此,如果您的ModelForm有效,那么您知道该模型也是有效的,您可以保存。

是的,即使是多模型表格,这些答案仍然适用。

所以你应该做的是:

  • 仍然使用ModelForm
  • 不用担心验证模型,因为ModelForm会为你做这件事。

一般来说,如果你发现自己正在用django做一些管道,那是因为还有另一种更简单的方法!

第二个指针应该让你开始并实际简化你的代码...

答案 1 :(得分:5)

1)是的,对模型进行验证是完全有效的。这就是Django人们添加它的原因。表单并不总是在保存模型的过程中使用,因此如果只通过表单进行验证,则会遇到问题。当然,人们过去通过覆盖save方法并以这种方式进行验证来解决这一局限。但是,新模型验证更具语义性,并且在实际使用表单时为您提供了验证过程的钩子。

2)文档非常明确地说,在调用full_clean时运行模型验证(ModelForm.is_valid)。但是,如果您不使用ModelForm或想要进行其他处理,则需要手动调用full_clean。你这样做,但把它放在一个被覆盖的save是错误的方法。请记住:“明确比隐含更好。”此外,在许多其他地方和方式中调用save,对于ModelForm,实际上你最终会以这种方式运行full_clean

那就是说,既然文档说ModelForm自动full_clean,我认为看看它如何处理错误是有意义的。在_post_clean方法中,从django.forms.models的第323行开始:

# Clean the model instance's fields.
try:
    self.instance.clean_fields(exclude=exclude)
except ValidationError, e:
    self._update_errors(e.message_dict)

# Call the model instance's clean method.
try:
    self.instance.clean()
except ValidationError, e:
    self._update_errors({NON_FIELD_ERRORS: e.messages})

反过来,_update_errors的代码从同一模块的第248行开始:

def _update_errors(self, message_dict):
    for k, v in message_dict.items():
        if k != NON_FIELD_ERRORS:
            self._errors.setdefault(k, self.error_class()).extend(v)
            # Remove the data from the cleaned_data dict since it was invalid
            if k in self.cleaned_data:
                del self.cleaned_data[k]
    if NON_FIELD_ERRORS in message_dict:
        messages = message_dict[NON_FIELD_ERRORS]
        self._errors.setdefault(NON_FIELD_ERRORS, self.error_class()).extend(messages)

您必须稍微使用代码,但这应该为您提供一个很好的起点,可以将表单和模型验证错误结合起来。