Django表单验证
简单的验证
我们的搜索示例仍然相当简单,特别是在数据验证方面。 我们只是检查,以确保搜索查询不是空的。 许多HTML表单都包含比确保值非空的验证级别更复杂的级别。 我们都看到了网站上的错误消息:
- “请输入有效的电子邮件地址。 'foo'不是电子邮件地址。“
- “请输入有效的五位美国邮政编码。 '123'不是邮政编码。“
- “请输入YYYY-MM-DD格式的有效日期。”
- “请输入至少8个字符的密码,并至少包含一个数字。”
让我们调整我们的search()视图,以便验证搜索项长度小于或等于20个字符。 (为了举例,假设比这更长的话可能会使查询速度太慢)。我们该怎么做? 最简单的可能是将逻辑直接嵌入到视图中,如下所示:
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
if not q:
error = True
elif len(q) > 20:
error = True
else:
books = Book.objects.filter(title__icontains=q)
return render(request, 'search_results.html', {'books': books, 'query': q})
return render(request, 'search_form.html', {'error': error})
现在,如果您尝试提交长度超过20个字符的搜索查询,则不会让您搜索; 你会得到一个错误消息。 但是search_form.html中的那个错误信息现在说“请提交一个搜索字词” - 所以我们必须改变它对于这两种情况的准确性:
<html>
<head>
<title>Search</title>
</head>
<body>
{% if error %}
<p style="color: red;">
Please submit a search term 20 characters or shorter.
</p>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
这件事有一些丑陋的东西。 我们一刀切的错误信息可能会令人困惑。 为什么要提交一个空的表单提交的错误信息提到20个字符的限制? 错误消息应该是特定的,明确的而不是混淆的。 问题在于,我们使用简单的布尔值作为错误,而我们应该使用错误消息字符串列表。 以下是我们可以解决的问题:
def search(request):
errors = []
if 'q' in request.GET:
q = request.GET['q']
if not q:
errors.append('Enter a search term.')
elif len(q) > 20:
errors.append('Please enter at most 20 characters.')
else:
books = Book.objects.filter(title__icontains=q)
return render(request, 'search_results.html', {'books': books, 'query': q})
return render(request, 'search_form.html', {'errors': errors})
然后,我们需要对search_form.html模板进行一些调整,以反映它现在传递了错误列表而不是错误布尔值:
<html>
<head>
<title>Search</title>
</head>
<body>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
</body>
</html>
制作联系表格
尽管我们多次对书籍搜索表单进行了迭代,并对其进行了很好的改进,但它仍然非常简单:只有一个字段'q'。随着表单变得越来越复杂,我们必须对我们使用的每个表单字段重复上述步骤。这引起了很多的烦恼和很多人为错误的机会。对我们来说幸运的是,Django的开发人员想到了这一点,并在Django中构建了一个更高级别的库,用于处理与表单验证相关的任务。
你的第一堂课
Django附带了一个名为django.forms的表单库,它处理我们一直在探索本章的许多问题 - 从HTML表单显示到验证。让我们深入并使用Django表单框架重写我们的联系表单应用程序。使用表单框架的主要方法是为每个正在处理的HTML <form>定义一个Form类。
在我们的例子中,我们只有一个<form>,所以我们将有一个Form类。 Django社区约定是将Form类保存在一个名为forms.py的单独文件中。在与mysite \ views.py相同的目录中创建该文件,然后输入以下内容:
# mysite_project\mysite\mysite\forms.py
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField()
email = forms.EmailField(required=False)
message = forms.CharField()
这非常直观,与Django的模型语法相似。 表单中的每个字段都由一个Field类的类型表示 - CharField和EmailField是这里使用的唯一类型的字段 - 作为Form类的属性。 每个字段默认是必需的,所以为了使电子邮件可选,我们指定required = False。
让我们跳进Python交互式解释器,看看这个类可以做什么。 它能做的第一件事就是将自身显示为HTML:
(env_mysite) C:\Users\...\mysite> python manage.py shell
>>> from mysite.forms import ContactForm
>>> f = ContactForm()
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input type="text" name="subject" required id="id_subject" /></td></tr>
<tr><th><label for="id_email">Email:</label></th><td><input type="email" name="email" id="id_email" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" required id="id_message" /></td></tr>
Django为每个字段添加一个标签,以及用于访问的<label>标签。 这个想法是使默认行为尽可能最佳。 这个默认的输出格式是HTML <table>格式,但还有一些其他的内置输出:
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input type="text" name="subject" required id="id_subject" /></li>
<li><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" required id="id_message" /></li>
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input type="text" name="subject" required id="id_subject" /></p>
<p><label for="id_email">Email:</label> <input type="email" name="email" id="id_email" /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" required id="id_message" /></p>
请注意,打开和关闭的<table>,<ul>和<form>标记不包含在输出中,因此您可以根据需要添加任何额外的行和自定义。 这些方法只是显示整个表单的常见情况的捷径。 您还可以显示特定字段的HTML:
>>> print(f['subject'])
<input type="text" name="subject" required id="id_subject" />
>>> print f['message']
<input type="text" name="message" required id="id_message" />
Form对象可以做的第二件事是验证数据。 要验证数据,请创建一个新的Form对象并将其传递一个将字段名称映射到数据的数据字典:
>>> f = ContactForm({'subject': 'Hello', 'email': 'nige@example.com', 'message': 'Nice site!'})
一旦你关联了一个Form实例的数据,你已经创建了一个“bound”的形式:
>>> f.is_bound
True
调用任何绑定窗体上的is_valid()方法,以确定其数据是否有效。 我们已经为每个字段传递了一个有效的值,所以表单的整体是有效的:
>>> f.is_valid()
True
如果我们不通过电子邮件字段,它仍然有效,因为我们已经为该字段指定了required = False:
>>> f = ContactForm({'subject': 'Hello', 'message': 'Nice site!'})
>>> f.is_valid()
True
但是,如果我们忽略了主题或消息,表单不再有效:
>>> f = ContactForm({'subject': 'Hello'})
>>> f.is_valid()
False
>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f.is_valid()
False
您可以深入了解特定于字段的错误消息:
>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f['message'].errors
['This field is required.']
>>> f['subject'].errors
[]
>>> f['email'].errors
[]
每个绑定的Form实例都有一个errors属性,它提供了一个将字段名称映射到错误消息列表的字典:
>>> f = ContactForm({'subject': 'Hello', 'message': ''})
>>> f.errors
{'message'`: ['This field is required.']}
最后,对于已经发现数据有效的表单实例,可以使用一个“ cleaned_data”属性。 这是提交的数据字典,“清理”。 Django的表单框架不仅验证数据, 它通过将值转换为适当的Python类型来清除它:
>>> f = ContactForm({'subject': 'Hello', 'email': 'nige@example.com', 'message': 'Nice site!'})
>>> f.is_valid()
True
>>> f.cleaned_data
{'subject': 'Hello', 'email': 'nige@example.com', 'message': 'Nice site!'}
我们的联系表单只处理被“清理”成字符串对象的字符串 - 但是如果我们要使用IntegerField或DateField,则表单框架将确保已清空的数据为给定的字段使用适当的Python整数或datetime.date对象。