将Django模型验证错误传递给表单的惯用方法
请帮我理解一下,我做出的以下选择是否合适,如果不合适,应该怎么改进。
1) 模型验证优于表单验证
我更喜欢在可能的情况下使用新的模型验证,而不是表单验证,因为这样似乎是一种更简洁和基本的方式来为数据创建规则。以下是一个简单的日历条目模型的两个例子:
- “开始时间必须在结束时间之前”
- “时间段必须小于(结束时间-开始时间)”
把这些规则放在模型层面上是否合适,这样就不需要在表单中重复写?如果使用ModelForm是最好的选择,那么在同一个表单中使用多个模型的情况又该如何处理呢?
(编辑:我之前没有意识到可以将多个ModelForm一起使用)
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 个回答
1) 是的,在模型上进行验证是完全合理的。这就是Django团队添加这个功能的原因。因为在保存模型的过程中,并不总是会使用表单,所以如果验证只通过表单来进行,就会出现问题。当然,以前人们通过重写save
方法来解决这个限制,加入验证逻辑。不过,现在的新模型验证更符合语义,并且在实际使用表单时,可以更好地接入验证过程。
2) 文档中很清楚地说明,当调用ModelForm.is_valid
时,会运行模型验证(full_clean
)。但是,如果你不使用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)
你需要稍微动动代码,但这应该能给你一个很好的起点,帮助你结合表单和模型的验证错误。
[编辑 - 希望这能回答你的评论]
我会简短直接,但不想显得不礼貌 :)
1) 模型验证优于表单验证
我想说的一个简单原则是,只要这个规则确实和模型有关,那就最好在模型层面进行验证。
关于多个模型的表单,请查看这个其他的SO问题:Django:在一个模板中使用表单处理多个模型,还有这个附加链接,虽然有点旧,但仍然适用,讲述了实现它的最简单方法。这里讨论太多内容了,不方便再详细说!
2)
- a) 不!你应该从ModelForm派生你的表单,这样它会为你执行模型验证 -> 你不需要自己调用模型验证。
- b) 不!如果你想验证一个模型,你不应该尝试保存它;你应该使用full_clean -> 所以如果你的ModelForm是有效的,那你就知道模型也是有效的,这样就可以保存了。
是的,这些答案在处理多个模型的表单时仍然适用。
所以你应该做的是:
- 仍然使用ModelForm
- 不用担心验证模型,因为ModelForm会为你处理这个。
一般来说,如果你发现自己在用Django做一些繁琐的事情,那是因为还有其他更简单的方法可以做到!
第二个建议应该能帮助你入门,并且实际上会大大简化你的代码……