这一篇教程,我们一起来了解Django的表单(Forms)。
这篇教程参考了官方文档,内容比较详细。
一、HTML表单
首先,我们先更多的了解一些HTML中表单的相关知识。
在我们之前所接触的内容中,已经使用过HTML表单。
一个HTML表单包含一对<form>标签,并在这对标签中包含一个或多个表单元素标签<input>以及其它可能需要的元素标签。
根据Django的官方文档描述,一个表单需要指定两件事:
- 什么位置:与用户输入相对应的数据返回到哪一个URL路径;
- 什么方法:采用哪一种HTTP方法返回的数据。
提示:这里的返回我理解为从客户端返回到服务器。
实际上就是给出表单的提交动作(action属性)和提交的方法(method属性)。
1、action
action的属性值可以是一个文件,例如CGI编程中属性值是一个CGI脚本文件的名称);
它也可以是一个路径,例如《Django2:Web项目开发入门笔记(8)》中我们就是这样使用。
2、method
method的属性值可以是“GET”或者“POST”。
如果大家够细心的话,应该看出了这两种提交方法的区别。
简单来说,“GET”方法提交的表单数据会以参数形式呈现在浏览器的URL中,例如:https://www.baidu.com/s?wd=opython&nojc=1;
而“POST”方法则看不到提交的数据内容。
看上去“POST”比“GET”更加安全(在HTTPS中同样安全)。
那为什么不只用“POST”而丢弃“GET”呢?
实际上,他们各有各的特点,能够应用于不同的目的。
“Post”通常会应用于:
- 改变系统状态:对数据库进行增、删、改的操作;
- 保护加密数据:类似注册、登录、支付等需要加密的表单数据,要避免将数据暴露在URL或者服务器日志中;
- 保护系统权限:避免攻击者模仿表单内容获取敏感信息。
“GET”通常会应用于:
- 数据查询搜索:查询结果能够比较方便的存为书签,进行共享或者再次提交。
另外,“GET”适合少量数据提交,不适合提交大量数据,类似图片的二进制数据。
二、Django对于表单的作用
Django能够将大部分表单相关的工作简单化以及自动化,并且比程序员自己写的代码有更高的安全性。
它承担三部分工作:
- 准备和重组数据准备渲染
- 创建HTML表单的数据
- 接收和处理从客户端提交的表单和数据
这些都不再需要自己写代码去完成。
三、Django中的表单
在一个Web应用程序的上下文中,“form”并不单指HTML的<form>,也可能是Django的Form类的实例,或是提交时返回的结构化数据;在端对端工作中还可能是这些部分的集合。
Django中表单的核心组件是Forms类。
和一个Django的模型描述一个对象的逻辑结构、行为以及各部分的表现方式类似,Forms类描述了一个表单并决定它如何工作与呈现。
例如,一个模型类的字段映射到一个数据表的字段,一个表单类的字段映射到一个HTML表单的<input>元素。(一个ModelForm类可以通过Form映射一个模型类中的全部字段到HTML表单中相对应的 <input> 元素 ; Django admin即是基于此实现。)
一个表单字段的类负责管理表单数据和表单被提交时的验证;DateField类和FileField 类处理不同种类的数据和执行不同的任务。
一个表单字段表示用户在浏览器中的一个HMTL部件(用户界面的一部分),每一个字段类型都有一个对应的默认Widget(部件)类,这些部件类可以被重写。
在我们所学过的内容中,我们知道在Django中将一个数据对象呈现的步骤如下:
- 在视图中获取数据对象(例如,从数据库中获取数据);
- 将数据对象与模板整合;
- 使用模板变量将数据对象添加到HTML标记中。
在模板中渲染一个表单也是这样的形式,但是有一些重要的区别。
在模板中渲染表单可以是一个没有包含任何数据的模型实例,并且在模板中很少执行任何有用的操作;从另外的角度说,我们期望渲染一个未填充的表单,由用户去填充它。
所以,当我们在视图中进行模型实例化时,我们通常需要从数据库中进行检索;而当我们处理一个表单时,我们只是在视图中进行实例化。
当我们实例化一个表单时,可以选择留空或者预置内容,例如以下情形(预置内容):
- 一个已保存的模型实例;(例如编辑后台信息时)
- 整理其它来源的数据;
- 从前一个HTML表单提交得到的数据。
四、使用Django构建表单
假设我们在一个页面获取用户输入的姓名,我们就需要一个表单。
示例代码:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>表单示例</title> </head> <body> <form action="/user_name/" method="post"> <label for="user_name">姓名:</label> <input id="user_name" type="text" name="user_name" value="{{ current_name }}"> <input type="submit" value="OK"> </form> </body> </html>
提示:<label>标签的“for”属性用于规定 label 与哪个表单元素绑定,点击<label>标签时,光标则会进入与其绑定的表单元素。
上方这段代码中,红色的部分就是表单内容,他告诉浏览器要使用“post”方法,将表单中的数据发送至“/user_name/”这个URL中。
如果模板上下文包含“current_name”变量,将会预填充“user_name”这个表单元素的文字。
现在,如果将模板文件用浏览器打开的话,会呈现下方的内容。
接下来,我们可以通过一个视图函数整合这个模板,并能够根据需求提供预填充的数据。
然后,在提交表单的时候,发送到服务器的post请求会包含表单中的数据。
当然,我们还需要一个与URL“/user_name/”对应的视图函数,根据表单中的数据进行进一步处理。
以上,是我们之前的做法。
这是一个比较简单的表单,实际上,一个表单往往会包含更多的字段,可能是几个、几十个或者上百个。
而往往这些字段中的大部分字段都需要预填充,我们希望用户在完成操作前可以进行多次的编辑,进行提交(类似草稿)。
另外,我们有些时候需要在客户端(浏览器)中完成字段的验证,而不是提交到服务器之后再进行验证,这样能够为服务器缓解很多压力。
还有,我们可能需要一些更复杂的字段,允许用户进行像选择日历这样的操作。
类似以上这些需求,使用Django能够帮助我们完成大部分的工作。
接下来,我们来看使用Django构建一个表单。
1、定义表单类
我们在定义表单类时,都应该继承自“django.forms.Form”或者“django.forms.ModelForm” 。
提示:“Form”类和“ModelForm”类都是继承自私有的“BaseForm ”类。
在项目文件夹中,新建一个名为“forms.py”的文件。
示例代码:
from django import forms class NameForm(forms.Form): user_name = forms.CharField(label='姓名', max_length=20)
在上方代码中,我们定义了一个名为“NameForm”的表单类,继承自Django的“Form”类。
在这个类中,我们定义了一个“user_name”的字符字段(CharField),并对这个字段进行了一些设置。
- label:与字段所在元素捆绑的<label>标签的文字;
- max_length:对字段字符数量的限制,浏览器会自动限制输入字符的数量,并且提交表单时Django会自动进行长度的验证。
关于“CharField”字段类的参数不仅仅这两种,大家可以打开“fields.py”文件查看这个字段类,路径是:\Lib\site-packages\django\forms\fields.py
每一个表单类的实例都有一个“is_valid()
”方法,负责对表单中所有的字段进行验证,如果所有字段都通过验证,该方法的返回值为“True”,并且会将所有的表单字段数据存储到它的“cleaned_data ”属性中。
我们刚刚定义的Django表单,呈现在浏览器中的时候,就像下方这样的HTML代码(实际上还会多出一些<tr>、<th>和<td>这样的表格标签)。
示例代码:
<label for="id_user_name">姓名:</label> <input type="text" name="user_name" maxlength="20" required id="id_user_name"/>
注意:表单类所生成的HTML代码不包含<form>标签和提交(submit)按钮,我们需要在模板文件中自己编写。
2、定义视图函数
表单类需要在视图中实例化,并通过视图函数将空表单实例或者带有数据的表单实例与模板整合进行呈现。
示例代码:
from django.shortcuts import render from django.http import HttpResponseRedirect # 导入重定向类 from .forms import NameForm # 导入表单类 def get_name(request): if request.method == 'POST': # 如果为“post”方法提交 form = NameForm(request.POST) # 使用提交的数据实例化表单类 if form.is_valid(): # 如果表单数据全部有效 return HttpResponseRedirect('/thanks/') # 重定向到新的URL else: # 如果是“get”方法提交,例如:http://127.0.0.1:8888/user_name/?user_name=aaa form = NameForm() # 实例化一个空表单 return render(request, 'name.html', {'form': form}) # 将表单数据整合到模板并呈现 def thanks(request): # 表单提交有效时URL对应的视图 return render(request, 'thanks.html')
3、定义模板内容
在模板中整合表单内容,和在模板中整合模型的数据非常相似。
只需要把表单实例通过名称添加到模板中相应的位置即可。
示例代码:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>表单示例</title> </head> <body> <form action="/user_name/" method="post"> {% csrf_token %} {{ form }} <input type="submit" value="OK"> </form> </body> </html>
上方代码中,“{{ form }}”就是整合表单实例,而“{% csrf_token %}”是一个安全防御的标记,用来避免跨站伪造请求的攻击。
提示:如果只想呈现表单中的某一元素,可以通过类似“{{ form.user_name }}”的方式来获取这个元素。
4、配置URL分发
示例代码:
from django.contrib import admin from django.urls import path from FormTest import views as form_views urlpatterns = [ path('user_name/', form_views.get_name), path('thanks/', form_views.thanks), path('admin/', admin.site.urls), ]
完成以上的步骤,我们就可以启动开发服务器,通过“http://127.0.0.1:端口号/user_name/”或者“http://localhost:端口号/user_name/”进行测试了。
另外,大家也可以通过“http://127.0.0.1:8888/user_name/?user_name=aaa”这样的方式模拟“get”方法提交数据,结果是我们在视图中定义的空表单。
本节练习源代码:【点此下载】
转载请注明:魔力Python » Django2:Web项目开发入门笔记(17)