## 12.3 模式3:重写清理阶段的验证


让我们来讨论下有趣的验证用例:

》》多字段验证 》》涉及到已被验证的数据库已存数据的验证

对于使用自定义验证逻辑重写clean()clean_()方法来说,这两个是很好的开发场景。

在默认的和自定义的验证器运行之后,Django在这段时间通过clean()clean_()方法,提供了第二阶段和验证外来数据的加工。你可能奇怪为什么Django提供了这么多的钩子进行验证,所以这里是我们最喜欢的论点:

1.clean()方法是应用于验证互相作用的两个或两个以上字段的,因为它不是特定于任何一个特定的字段。 2.清理验证阶段是一个很好的地方对持久性数据附加验证。因为数据已经有了一些验证,你不会循环在不必要的查询上浪费很多数据库。

让我们通过另一个例子探索它。或许我们想实现一个冰淇淋订购表单,人们可以定制想要的口味,加上配料,然后来到我们的商店购买。

因为我们要防止用户订购的口味是缺货的,我们将会加入一个clean_slug()方法。结合我们的口味验证(FLavor),我们的表单应该是这样的:

  1. EXAMPLE 12.7
  2. # flavors/forms.py
  3. from django import forms
  4. from flavors.models import Flavor
  5. class IceCreamOrderForm(forms.Form):
  6. """Normally done with forms.ModelForm. But we use forms.Form here
  7. to demonstrate that these sorts of techniques work on every
  8. type of form.
  9. """
  10. """
  11. 通常使用forms.ModelForm。但是我们在这里使用了forms.Form来证明这些技术在每个类型的表单都可以使用。
  12. """
  13. slug = forms.ChoiceField("Flavor")
  14. toppings = forms.CharField()
  15. def __init__(self, *args, **kwargs):
  16. super(IceCreamOrderForm, self).__init__(*args,
  17. **kwargs)
  18. # We dynamically set the choices here rather than
  19. # in the flavor field definition. Setting them in
  20. # the field definition means status updates won't
  21. # be reflected in the form without server restarts.
  22. “”“
  23. 我们在这里动态设置选项而不是在口味字段定义中设置。在字段定义中设置它们意味着如果服务器不重启,状态更新将不会体现在表单上。
  24. ”“”
  25. self.fields["slug"].choices = [
  26. (x.slug, x.title) for x in Flavor.objects.all()
  27. ]
  28. # NOTE: We could filter by whether or not a flavor
  29. # has any scoops, but this is an example of
  30. # how to use clean_slug, not filter().
  31. “”“
  32. 注:我们可以通过是否一个口味有勺子来过滤,但是这是使用clean_slug()的例子,而不是filter()。
  33. ”“”
  34. def clean_slug(self):
  35. slug = self.cleaned_data["slug"]
  36. if Flavor.objects.get(slug=slug).scoops_remaining <= 0:
  37. msg = u"Sorry, we are out of that flavor."
  38. raise forms.ValidationError(msg)
  39. return slug

对于HTML-powered视图,我们例子中的clean_slug()方法,抛出一个错误,将会在口味(Flavor)HTML输入字段附加上“Sorry, we are out of that avor”信息。这是编写HTML表单的一个好的捷径。

现在想象一下,如果我们得到共同的客户抱怨订单有太多的巧克力。是的,它是愚蠢的,而且是不可能的,但我们只是使用了“太多的巧克力”作为一个完全虚构的例子来阐明观点。

在任何情况下,让我们使用clean()方法来验证互相作用的口味(Flavor)和配料(toppings)字段。

  1. EXAMPLE 12.8
  2. # attach this code to the previous example (12.7)
  3. # 在上面例子(12.7)添加这段代码
  4. def clean(self):
  5. cleaned_data = super(IceCreamOrderForm, self).clean()
  6. slug = cleaned_data.get("slug", "")
  7. toppings = cleaned_data.get("toppings", "")
  8. .
  9. # Silly "too much chocolate" validation example
  10. # 愚蠢的“太多巧克力”验证例子
  11. if u"chocolate" in slug.lower() and \
  12. u"chocolate" in toppings.lower():
  13. msg = u"Your order has too much chocolate."
  14. raise forms.ValidationError(msg)
  15. return cleaned_data

完工了,一个不可能抱怨太多巧克力的实现!

提示:多字段验证中的常见字段

对于用户账户表单,一个普遍的做法是在涉及到邮箱和密码的字段让用户强制输入相同的两次数据。对这些字段的其他检查,包括: 》》提交的密码强度。 》》如果邮箱模型没有设置unique=True,是否邮箱是唯一的。

Figure 12.2