除非您计划搭建的网站和应用只发布内容且不接收访问者的输入,否则您就需要理解和使用表单。
Django提供了一系列的工具和库来帮助您构建表单来接收网站访客的输入,然后处理以及响应这些输入。
HTML表单
在HTML中,表单是在 <form>...</form> 中的一些元素,它允许访客做一些类似输入文本、选择选项、操作对象或空间等动作,然后发送这些信息到服务端。
一些表单界面元素(文本框或复选框)内置在HTML中。其他会更复杂些;像弹出日期选择或者允许您移动滑块或者操作控件,一般通过使用JavaScript,CSS以及HTML表单中的 <input> 元素来实现这些效果。
和它的元素 <input> 一样,表单必须指定两样东西:
- 何地:负责响应用户输入数据的URL地址
- 如何:数据请求使用的HTTP方法。
例如,Django admin的登录表单包含了一些 <input> 元素:用户名用 type="text" ,密码用 type="password" ,登录按钮用 type="submit" 。它还包含一些用户看不到的隐藏文本字段,Django用它们来决定下一步行为。
它还告诉浏览器表单数据应该发往 <form> 的 action 属性指定的URL—— /admin/ ,并且应该使用它的 method 属性指定的HTTP方法—— post 。
当 <input type="submit" value="Log in"> 元素被触发的时候,数据会发送到 /admin/ 。
GET 和 POST
处理表单时只会用到 GET 和 POST 两种HTTP方法。
Django的登录表单使用 POST 方法传输数据,在这个方法中浏览器会封装表单数据,为了传输会进行编码,然后发送到服务端并接收它的响应。
相比之下,GET 方法将提交的数据捆绑到一个字符串中,并用它来组成一个URL。该URL包含了数据要发送的地址以及一些键值对应的数据。如果您在Django文档中进行一次搜索,就会看到这点,它会生成一个形似 https://docs.djangoproject.com/search/?q=forms&release=1 的URL。
GET 和 POST 通常用于不同的目的。
任何可能用于更改系统状态的请求应该使用 POST —— 比如一个更改数据库的请求。GET 应该只被用于不会影响系统状态的请求。
GET
方法也不适合密码表单,因为密码会出现在URL中,于是也会出现在浏览器的历史记录以及服务器的日志中,而且都是以纯文本的形式。它也不适合处理大量的数据或者二进制数据,比如一张图片。在WEB应用的管理表单中使用 GET
请求具有安全隐患:攻击者很容易通过模拟请求来访问系统的敏感数据。 POST
方法通过与其他像Django的 CSRF protection 这样的保护措施配合使用,能对访问提供更多的控制。
另一方面, GET 方法适用于诸如网页搜索表单这样的内容,因为这类呈现为一个 GET 请求的URL很容易被存为书签、分享或重新提交。
Django在表单中的角色
处理表单是一件挺复杂的事情。想想看Django的admin,许多不同类型的数据可能需要在一张表单中准备显示,渲染成HTML,使用方便的界面进行编辑,传到服务器,验证和清理数据,然后保存或跳过进行下一步处理。
Django的表单功能可以简化和自动化上述工作的大部分内容,并且也能比大多数程序员自己编写代码去实现来的更安全些。
Django会处理涉及表单的三个不同部分:
- 准备并重组数据,以便下一步的渲染
- 为数据创建HTML 表单
- 接收并处理客户端提交的表单及数据
您 可以 手动编写代码来实现,但Django 可以帮你完成所有这些工作。
Django 中的表单
我们已经简单的描述过了HTML 表单,但是一个HTML <form>
只是其所需的一部分。
Web应用中所说的'表单',可能指的是HTML <form>
,或者是生成了它的Django Form
,再或者是提交时返回的结构化数据,亦或是这些端到端作业的合集。
Django的 Form
类
Django表单系统的核心组件是 Form
类。它与Django模型描述对象的逻辑结构、行为以及它呈现给我们内容的形式的方式大致相同, Form
类描述一张表单并决定它如何工作及呈现。
类似于模型类的字段映射到数据库字段的方式,表单类的字段会映射到HTML表单的 <input>
元素。 ModelForm
通过 Form
映射模型类的字段到HTML表单的 <input>
元素,Django admin就基于此。
表单字段本身也是类;他们管理表单数据并在提交表单时执行验证。 DateField
和 FileField
处理的数据类型差别很大,所以必须用来处理不同的字段。
在浏览器中,表单字段以HTML“控件”(用户界面的一个片段)的形式展现给我们。每个字段类型都有与之相匹配的 控件类 ,但必要时可以覆盖。
实例化、处理和渲染表单
在Django中渲染一个对象的时候,我们通常:
1.在视图中获取它(例如从数据库中取出)
2.将它传递给模板上下文
3.使用模板变量将它扩展为HTML标记
在模板中渲染表单几乎与渲染任何其他类型的对象的一样,但是存在一些关键性的差异。
如果模型实例不包含数据,在模板中对它做任何处理几乎没什么用。但完全有理由用来渲染一张空表单——当我们希望用户来填充的时候就会这么做。
所以当我们在视图中处理模型实例时,我们一般从数据库中获取它。当我们处理表单时,我们一般在视图中实例化它。
当我们实例化表单时,我们可以选择让它为空或者对它预先填充,例如使用:
来自已保存的模型实例的数据(例如在管理编辑表单的情况下)
我们从其他来源获取的数据
从前面一个HTML 表单提交过来的数据
最后一种情况最有趣,因为这使得用户不仅可以阅读网站,而且还可以将信息发回给它。
构建一张表单
需要完成的工作
假设您希望在您的网站上创建一张简易的表单,用来获取用户的名字。您需要在模板中使用类似代码:
<form action="/your-name/" method="post">
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" value="{{ current_name }}">
<input type="submit" value="OK">
</form>
这告诉浏览器将表单数据返回给URL /your-name/ ,并使用 POST 方法。它将显示一个标签为 "Your name:" 的文本字段,以及一个 "OK" 按钮。如果模板上下文包含一个 current_name 变量,它会被预填充到 your_name 字段。
您需要一个视图来渲染这个包含HTML表单的模板,并能适当提供 current_name 字段。
提交表单时,发送给服务器的 POST 请求将包含表单数据。
现在,您还需要一个与该 /your-name/ URL相对应的视图,该视图将在请求中找到相应的键/值对,然后对其进行处理。
这是一张非常简单的表单。实际应用中,一张表单可能包含数十上百的字段,其中许多可能需要预填充,并且我们可能希望用户在结束操作前需要多次来回编辑-提交。
我们可能需要在浏览器中进行一些验证,甚至在表单提交之前;我们可能希望使用更复杂的字段 ,以允许用户做类似日期选择等操作。
此刻,我们很容易通过使用Django来完成以上大部分工作。
在Django 中构建一张表单
Form
类¶
我们已经很清楚想要的HTML表单看起来会是什么样子。首先,在Django中这样做:
forms.py
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
它定义了一个只包含一个字段( your_name
)的 Form
类。我们已经为这个字段提供了友好的标签,当它渲染后会显示在 <label>
中(在这种情况下,如果我们省略之前指定的 label
,它还是会自动生成一个一样的标签)。
字段的最大长度由 max_length
来定义。它做了两件事情。首先它在HTML的 <input>
上增加了 maxlength="100"
(这样浏览器会在第一时间阻止用户输入超过这个数量的字符串)。其次它还会在Django收到浏览器传过来的表单时,对数据长度进行验证(也就是服务器端验证)。
A Form
instance has an is_valid()
method, which runs validation routines for all its fields. When this method is called, if all fields contain valid data, it will:
- 返回
True
- 将表单的数据放到它的属性
cleaned_data
中。
这样整个表单在第一次渲染时,会显示如下:
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required>
注意它 没有 包含 <form> 标签和提交按钮。我们必须自己在模板中提供。
视图
发回Django网站的表单数据由视图来处理,一般和发布这个表单用的是同一个视图。这允许我们重用一些相同的逻辑。
为了处理表单,我们需要将它实例化到我们希望发布的URL的对应的视图中:
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
模板
我们没有必要在模板 name.html 中做过多的操作:
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
所有的表单字段及其属性都将通过Django模板语言从 {{ form }} 中被解包成HTML标记。
所有的表单字段及其属性都将通过Django模板语言从 {{ form }} 中被解包成HTML标记。
- 表格和跨站请求伪造保护
Django自带一个简单易用的 跨站请求伪造防护 。当通过
POST
方法提交一张启用了CSRF防护的表单时,您必须使用上例中这样的模板标签csrf_token
。但是,由于CSRF防护在模板中没有与表单直接绑定,因此这个标签在本页文档之后的示例中都将被忽略。
*HTML5输入类型和浏览器验证
如果您的表单包含
URLField
,EmailField
或者其他整数字段类型,Django将使用url
,number
HTML5输入类型。默认情况下,浏览器可能会在这些字段上应用他们自己的验证,这也许比Django的验证更加严格。如果您想禁用这个行为,请在form
标签上设置 novalidate 属性,或者在字段上指定一个不同的控件,比如TextInput
。
现在我们有了一个可以工作的web表单,它通过一张Django Form
描述,由一个视图来处理并渲染成一个HTML <form>
。
以上是您入门需要了解的所有内容,但是表单框架提供了更多垂手可得的内容。一旦您理解了上述过程的基础知识,您应该再了解下表单系统的其他功能,然后学习更多的底层机制。
详解Django Form
类¶
模型和表单
实际上,如果您的表单是要直接用来添加或编辑Django模型,用 ModelForm ,可以省时、省力、省代码,因为它会根据
Model
类构建一张对应字段及其属性的表单。
字段详解
考虑一下比我们上面的小示例更有用的一张表单,我们可以用它在个人网站上实现“联系我”的功能:
forms.py
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
我们之前的表单只用了一个 CharField
类型的字段 your_name
。在这个例子中,我们的表单有四个字段: subject
、 message
、 sender
和 cc_myself
。只用到三种可用的字段类型: CharField
、 EmailField
和 BooleanField
;完整的字段类型清单请参看 Form fields 。
控件¶
每个表单字段都有一个相对应的 控件类 ,这个控件类又有对应的HTML表单控件,比如 <input type="text">
。
多数情况下,字段都有合适的默认控件。比如,默认情况下, CharField
有个 TextInput
控件,它会在HTML中生成一个 <input type="text">
。如果您想要的是 <textarea> `` ,您要在定义表单字段的时候指定控件,就像我们对 ``message
字段那样处理。
字段数据
无论用表单提交了什么数据,一旦通过调用 is_valid() 验证成功( is_valid() 返回 True ),已验证的表单数据将被放到 form.cleaned_data 字典中。这里的数据已经很好的为你转化为Python类型。
注解
此时您依然能够直接从 request.POST 中访问到未验证的数据,但最好还是使用经验证的数据。
在上面的联系表单示例中, cc_myself
会被转化成一个布尔值。同样的,字段 IntegerField
和 FloatField
的值分别会被转化为Python的 int
和 float
类型。
下面例举了如何在视图中处理表单数据:
views.py¶
from django.core.mail import send_mail
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['info@example.com']
if cc_myself:
recipients.append(sender)
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/')
有些字段类型需要一些额外的处理。例如,使用表单上传文件就要以不同的方式处理(它们可以从 request.FILES
获取,而不是 request.POST
中)。有关如何使用表单处理文件上传的详细信息,请参见 Binding uploaded files to a form 。
网友评论