## 11.5 了解表单验证如何工作


表单验证是Django的诸多领域之一,了解其内部的运作将会大幅度的提高你的代码质量。让我们花一些时间去钻研表单验证并且讲述一些要点。

当你调用form.is_valid()时,很多事件在表面之后发生。下列事件发生在这个工作流里:

    1.如果这个表单含有绑定数据,form.is_valid()会调用form.full_clean()方法。

    2.form.full_clean在表单字段中迭代并且验证每个字段:

        a.数据进入到表单字段内通过to_python()方法被强制转为Python代码或者产生一个ValidationError错误。

        b.数据针对特殊的表单字段规则进行验证,包括自定义验证器。失败的话会产生一个ValidationError错误。

        c.如果在表单中存在任何自定义clean_()方法,此时它们将会被调用。

    3.form.full_clean()执行 form.clean()方法。

    4.如果是模型表单的实例,form._post_clean()将会做如下事情:

        a.设置模型表单的数据到模型的实例中,不管是否form.is_valid()True还是False

        b.调用模型的clean()方法。作为参考,存储一个模型实例通过ORM不会调用模型的clean()方法。

如果这看起来很复杂,请记住,在实践中它会变得简单些,而且所有这些功能都让我们真正理解输入数据中发生了什么。在下一节的例子应该有助于解释这一步。

                Figure 11.1


11.5.1 模型表单数据被存储到表单实例,然后到模型实例

我们喜欢把这个叫做WHAT?!?的表单验证。(原文:We like to call this the WHAT?!? of form validation.可能我的电子版错误,如有错误请指正。)乍看起来,表单数据被安放到表单实例中看起来像是一个BUG。但是这不是BUG,这是预期行为。

在一个模型表单中,表单数据在两个不同的步骤中被存储:

    1.首先,表单数据被存储到表单实例。

    2.然后,表单数据被存储到模型实例。

因为模型表单不被存储到模型实例直到它们通过form.save()方法被激活,我们可以利用这种分离的优势形成一个有用的功能。

举个例子,或许你需要对一个表单抓捕提交失败的细节,保存用户提供的表单数据以及预期模型实例更改。

一个简单的,也许是简单的,捕捉数据的方式如下。首先,我们创建一个失败表单的历史模型在core/models.py中:

  1. EXAMPLE 11.6
  2. # core/models.py
  3. from django.db import models
  4. class ModelFormFailureHistory(models.Model):
  5. form_data = models.TextField()
  6. model_data = models.TextField()

接着,我们添加下面的代码到flavors/views.pyFlavorActionMixin

  1. EXAMPLE 11.7
  2. # flavors/views.py
  3. import json
  4. from django.contrib import messages
  5. from django.core import serializers
  6. from core.models import ModelFormFailureHistory
  7. class FlavorActionMixin(object):
  8. @property
  9. def success_msg(self):
  10. return NotImplemented
  11. def form_valid(self, form):
  12. messages.info(self.request, self.success_msg)
  13. return super(FlavorActionMixin, self).form_valid(form)
  14. def form_invalid(self, form):
  15. """Save invalid form and model data for later reference."""
  16. """保存无效的格式和模型数据以供以后参考。"""
  17. form_data = json.dumps(form.cleaned_data)
  18. model_data = serializers.serialize("json",
  19. [form.instance])[1:-1]
  20. ModelFormFailureHistory.objects.create(
  21. form_data=form_data,
  22. model_data=model_data
  23. )
  24. return super(FlavorActionMixin,
  25. self).form_invalid(form)

如果你还记得, form_invalid()被调用在一个错误表单数据的验证失败之后,当它在这个例子中被调用时,表单清理数据和保存到数据库的最终数据都被保存为一个ModelFormFailureHistory记录。