python 全栈开发,Day78(Django组件
- 作者: 五速梦信息网
- 时间: 2026年04月04日 13:32
一、Django组件-forms组件
forms组件
django中的Form组件有以下几个功能:
- 生成HTML标签
- 验证用户数据(显示错误信息)
- HTML Form提交保留上次提交数据
- 初始化页面显示内容
校验字段功能
之前写的视图函数,提交的数据,没有做校验,就添加到数据库里面了。这样是不对的!
比如:用户名,必须要符合一定的长度。密码复杂度,等等。
forms组件最大的作用,就是做数据校验。
普通做法,一个一个写校验规则,没有解耦。校验规则,都在视图函数里面。
新建项目formDemo
修改urls.py,新增路径index
修改views.py,新增index视图函数
form组件先放到视图函数
单独起一个类,后续会分离出来
templates新增index.html,里面是空的
打开Pycharm,点击左下角的Python Console

输入以下命令,导入视图函数的DemoForm类
效果如下:

如果有报错,请查看当前的python环境,是否加载了Django模块。
DemoForm类是用来做校验的,它接收一个字典。字典必须包含2个key,分别是name,age,email
测试一个字典数据
执行下面2个命令
效果如下:输出True

解释:
is_valid()表示执行校验,如果3个key都符合要求,输出True
测试1:age不符合

3个必须同时成立才行
在DemoForm里面,等式左边对应的是key,等式右边对应校验规则
它有一个默认规则,不能为空
测试2:少一个字段

测试3:加一个额外的key-value呢?

从结果上来看,也是可以通过的。
它只校验指定的字段,那么额外的键值,会忽略。
网页校验
修改urls.py,增加路径addbook
修改views.py,增加addbook视图函数,完整代码如下:
templates新增addbook.html
做表单校验的时候,一定要注意,表单的name和class的属性必须一一对应
网页访问添加页面,输出信息

提交之后,效果如下:

Pycharm控制台输出:success
空表单直接提交

Pycharm控制台输出:fail
is_valid()
form.is_valid() 它做了2件事情:
1.将数据传给form
2.将验证数据拆分到2个容器中
self.cleaned_data= {} 表示干净的数据
self.error = {} 表示验证不通过的数据
self表示UserForm类的实例对象
addbook视图函数
再次提交数据

Pycharm控制台输出:
虽然POST发送了5个键值,但是UserForm只校验3个键值。
form.cleaned_data 输出了3个键值
form.errors 输出的内容空,它的类型为ErrorDict
只要有一个错误,就会走else
错误数据演示
修改views.py,修改UserForm,增加邮箱
输入一个错误的表单

Pycharm控制台输出:
form.errors输出了一段Html标签,提示邮箱格式错误
提取email错误信息
修改UserForm
Pycharm控制台输出:
form.errors.get(“email”) 可以提取email的错误信息,它返回的是一个错误列表
通过切片,可以获取第一个错误信息
渲染标签功能
渲染方式1
使用自带的模板属性渲染
上面讲的form表单里面的元素,是手动写的。form组件可以帮你实现渲染表单元素!
那么需要渲染哪些元素,取决于UserForm这个自定义类的属性来决定的
举例:
修改addbook视图函数
修改addbook.html
as_p是一个特殊的属性,常见的有:
{{ form.as_table }}{{ form.as_p }}{{ form.as_ul }}
访问页面,效果如下:

使用浏览器工具,查看html代码
它使用了P标签来包裹

lable的for属性和input的id属性是对应的。id的名字和UserForm类定义的属性是类似的,加了id_前缀。
lable的显示的文字和UserForm类定义的属性是一样的,首字母大写了!
input的name属性和UserForm类定义的属性是一样的
默认自带required属性,不允许内容为空。
minlength的属性来源于UserForm类的定义。
注意:form组件只能渲染表单里面的元素,比如input标签。除此之外,其他的需要手写!
它的样式,太丑了!
渲染方式2
使用自定义的标签来包裹form变量
举例:
更改addbook.html
刷新网页,效果如下:

渲染方式3
使用for循环渲染
修改addbook.html
刷新网页,效果如下:

field.label 表示UserForm类定义的属性名。注意:它不是html的label标签!
field 表示input输入框,由forms组件渲染
显示中文
将label换成中文,需要增加label属性
修改views.py里面的UserForm类
刷新网页,效果如下:

美化input输入框
需要使用bootstrap
修改urls.py,修改路径
修改views.py,将addbook重名为adduser
将addbook.html,重命名为adduser.html
引入bootstrap,代码如下:
访问url: http://127.0.0.1:8000/adduser/
效果如下:

这里面input用的还是默认样式,只要给input标签增加class=“form-group”,就有美化效果!
由于input是form组件渲染的,不能直接添加class,需要在UserForm类里面,指定class
修改UserForm类之前,导入一个模块widgets
Widgets
Widget 是Django 对HTML 输入元素的表示。Widget 负责渲染HTML和提取GET/POST 字典中的数据。
如果你想让某个Widget 实例与其它Widget 看上去不一样,你需要在Widget 对象实例化并赋值给一个表单字段时指定额外的属性(以及可能需要在你的CSS 文件中添加一些规则)
修改views.py,完整代码如下:
解释:
widget等式右边,可以指定多种类型的输入框,比如:TextInput,EmailInput,DateInput…
默认是TextInput
attrs 表示设置css样式,它接收一个字典,可以写多个css样式!
修改adduser.html
给div增加class=“form-group”,表示调整上下间距
col-md-offset-2 表示偏移距离
刷新网页,效果如下:

显示错误与保存输入信息功能
保存输入信息功能
比如博客园的注册页面,链接如下:
直接提交空数据,页面会提示

那么form组件,也是可以实现这个效果
修改adduser视图函数
直接提交空数据,页面有错误提示
注意:这个提示是bootstrap做的,不是form组件

虽然jquery可以直接对表单进行验证,判断为空,或者其他规则。但是客户端浏览器的js代码,是可以跳过验证的。直接提交数据给服务器,如果服务器没有做数据校验,那么将面临风险!
修改adduser.html,在form标签后面增加novalidate,表示关闭bootstrap表单验证
刷新页面,填3个值,最后一个故意不填写

点击提交,效果如下:

发现刚刚增加的数据,没有了。这样用户体验不好!用户得小心翼翼的输入每一个数据!
查看Pycharm控制台输出:
发现它走了else的代码,使用render时,没有传值。导致页面为空!
修改views.py
给adduser.html传一个form。注意:此时的form变量是带有表单数据的!
再次刷新页面,数据就回来了!

数据怎么就回来了呢?
因为既然是POST请求,而且携带了POST数据,必然执行了form.is_valid()
虽然没有验证通过,但是执行下面一句代码时
此时的form是含有POST表单数据的,所以页面才会渲染出来!
注意:当input属性为password时,是不会渲染的!除此之外,其他的表单元素,是可以渲染的!
提交一个正确的数据

提示添加成功

显示错误信息
约定俗成,使用span标签来显示错误信息
修改adduser.html,增加span标签
解释:
field.errors 表示错误列表。因为是列表,会有很多错误信息
field.errors.0 表示接收第一个错误信息。一般取第一个即可!
那么问题来了,get请求时,比如地址栏访问页面,它是取不到值的。那么span标签是空的,但是不影响页面展示
直接提交空数据,页面会有英文提示,它表示此字段不允许为空

显示黑色,不好看,加一个样式
修改adduser.html,增加样式。pull-right表示右对齐
刷新页面,效果如下:

错误信息显示中文
显示中文需要在UserForm类中的字段增加error_message属性
解释:
error_messages 用来定义错误信息,可以定义多个错误类型!它接收一个字典
required 表示为空时,输出This field is required.
那么要定义中文时,重新赋值即可。需要显示日文,韩文,法文…的,自己定义吧!
重新访问页面,输入第一个值,提交。其它字段会有错误提示!

邮箱输入字符串,提示一段英文信息。不行,得改!

修改UserForm类,修改这一行
重新提示数据

核心问题,必须要明白,错误信息为什么会显示出来?
执行is_valid(),就会执行校验动作。如果不通过,那么form变量就会包含错误信息。
通过form组件渲染错误信息,页面就展示出来
局部钩子与全局钩子
上面提到的校验规则是forms组件自带的。 它做了一些简单的校验功能,比如判断字符串,纯数字,邮箱等等。
比如要求用户名,必须包含字母和数字。年龄必须要大于18岁,手机号码要以136,137开头…
这些需求,默认的校验规则是做不到的。
我们想要自行设计校验的规则,Django给我们提供了钩子。
先来看一段源码:
这段源码能够设置钩子的来源。
局部钩子
导入模块
举例:
要求用户名不能是纯数字
注意:
clean_name 这个名字是有含义的,不能随便定义。name表示UserForm类的属性。clean表示校验
val 表示用户输入的用户名
val.isdigit() 表示判断输入的是否为数字,必须return 一个值
raise 表示主动报错,必须接ValidationError。
上面这些要求是源代码定义的,具体可以看源代码。
views.py,完整代码如下:
验证一下,输入4位数字,提交之后,页面提示如下:

手机号码必须11位
修改UserForm类,增加clean_tel方法,完整代码如下:
注意:要去除tel里面的min_length,这是不严谨的写法!
重新访问页面,测试一下

年龄必须18岁以上
增加clean_age方法
重新访问页面,测试一下

注意:
is_valid执行时,才会执行校验。
这里有2层校验。第一层校验是UserForm定义的那些属性,比如判断字符串或者数字的。
第二次校验是clean_属性名 定义的这些方法。只有通过第一层校验时,才会进入第二层校验。
不论式第一层还是第二层,通过校验后,将key_value放到 cleaned_data容器里面。不通过校验时,将key-value放到errors容器里面
查看源代码
先找到views.py里面的is_valid,使用Ctrl+鼠标左键,点击is_valid。它会调转到is_valid方法的源代码
点击self.errors–>full_clean()–>self._clean_fields()
_clean_fields源代码如下:
它会对表单的每一个数据,使用for循环处理。field指的是UserForm定义的那些属性
self.fields.items() 这里的self.fields数据,可能是这样的。
self.fields.items() ,就能拿到一个field对象。这个field是是一个规则对象.
self.cleaned_data[name] = value 表示通过第一层校验,将干净的数据放到cleaneddata容器里
if hasattr(self, ‘clean%s’ % name): 表示进入第二校验,name表示UserForm定义的那些属性。通过后,也会放到cleaned_data容器里
except ValidationError as e 表示接收ValidationError错误,add_error表示添加到error容器里。
所以在UserForm定义clean时,必须使用raise ValidationError(xxx)
假设通过第一层校验,但是没有通过第二层校验时。它会执行add_error方法,那么原来的cleaned_data容器的数据怎么办?
add_error源代码如下:
注意:看最后一行,它会将cleaned_data容器里,没通过的数据给删除掉!
全局钩子
局部钩子只能校验一个字段,那么2个字段校验呢?比如密码和确认密码必须一致,这个时候,需要使用全局钩子
如何定义全局钩子呢?查看源代码
is_valid()–>self.errors–>self.full_clean()–>self._clean_form()–>self.clean()
clean源代码如下:
谷歌翻译如下:
在Field.clean()之后进行任何额外的表单范围清理,呼吁每个领域。此方法引发的任何ValidationError都将与特定领域无关;它将有一个特例与名为‘all’的字段关联。
大概意思就是,它在clean_xx执行之后,才会执行。一旦引发了ValidationError,与特定领域无关。错误信息都在‘all’里面
这个clean是全局钩子,属于第3层校验规则。源代码没有任何逻辑,所以这个方法,需要我们来重写。注意:名字必须是clean,结尾部分必须是return self.cleaned_data
两次密码不一致
修改UserForm类,增加2个属性,并定义全局钩子clean
完整代码如下:
重新访问页面,效果如下:

测试2次密码不一致

发现密码没有错误提示,为什么呢?
因为全局钩子和局部钩子不一样,它的错误信息在all里面
修改adduser视图函数,完整代码如下:
修改adduser.html,完整代码如下:
解释:
我们想在第二次密码输入框下面,展示全局钩子的错误信息。那么需要用到if判断了
g_error|default_if_none:“” 表示当g_error为none时,页面显示为空(“”)
验证一下,输入2个不一致的密码

效果如下:

Pycharm控制台输出:
如果只输入了1个密码呢?
页面提示确认密码不能为空

两次密码必须输入时,才会进入全局钩子
这是为什么?此时的UserForm有3层校验规则。执行顺序如下:
forms组件自带的校验规则–>局部钩子–>全局钩子
那么当有一个密码没有输入时,直接被第一层校验规则拦截了,它是不会进入到第三层校验规则的!
设置全局钩子,必然会执行。如果上层报错,那么不会进入全局钩子!
思考问题:forms的校验规则和models.py的模型类,有没有关系?
答案是没有关系!forms可以独立运行,forms组件没有必要,必须和model表的字段一一对应。
根据业务需求,在需要校验的字段上,进行校验!
分离forms代码
在views.py同级目录创建文件form.py,将forms相关代码剪贴过去,完整内容如下:
修改views.py,导入UserForm类,完整代码如下:
再次访问页面,测试密码不一致

效果如下:

form组件补充知识
Django内置字段
你可以在里面选择属性的类型以及约束。
Django内置插件
在witgits中选择使用
常用插件选择
周末作业:
在图书管理系统里,增加一个注册页面
要求:
进阶功能
使用ajax+forms组件完成注册功能
ajax接收error信息,修改dom,来显示错误信息!
答案
使用form表单实现
作业提到的3点要求,在将全局钩子的时候,已经演示出来了。
那么只要合格之后,在视图函数中插入一条记录到用户表中,就可以实现功能了!
下面介绍在上面演示的项目基础上,实现这些功能
修改models.py,增加一个用户表
修改settings.py,注册app。最后一行添加应用名
使用下面2个命令生成表
手动增加一条记录

修改form.py,代码如下:
修改views.py,代码如下:
修改adduser.html,代码如下:
访问页面添加用户界面

查看用户表记录,发现多了一条

ajax+forms组件实现
在上面的代码上,增加ajax功能
修改urls.py,增加路径add_ajajx
修改views.py,增加视图函数add_ajajx
修改adduser.html
访问页面:

测试效果如下:

查看用户表,发现多了一条记录

完整代码,请参考github
- 上一篇: python 少json包
- 下一篇: python 清空canvas






