17 Django Form组件
一、Form 介绍
我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。
与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
Django form组件就实现了上面所述的功能。
总结一下,其实form组件的主要三种功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
二、注册功能校验数据案例
注意:校验数据通常是前后端都有校验,前端校验可有可无,但是后端必须要有校验!!!!
1.普通方式手写注册功能
用户的注册功能:
用户输入的用户名中,不能包含”哈哈“字符,如果包含,提示用户,用户名不可以包含”哈哈“
用户输入密码,不能少于3位数字,如果少于3位,提示用户,密码不可以少于3位
# views.py文件
def reg(request):
back_dic = {'username':'','password':''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if '哈哈' in username:
# 此处是给全局的back_dic赋值
back_dic['username'] = '用户名中不能包含哈哈'
if len(password)<3:
# 此处是给全局的back_dic赋值
back_dic['password'] = '用户密码不可以少于三位'
# 所以这里不需要返回结果给前端页面
# 在这里就可以了,因为back_dic是全局的
return render(request,'reg.html',{'back_dic':back_dic})
# 此处返回back_dic可以用locals(),也可以用字典的型式
//reg.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<p>用户名:<input type="text" name="username"><span>{{back_dic.username}}</span></p>
<p>密码:<input type="text" name="password"><span>{{ back_dic.password }}</span></p>
<p>提交:<input type="submit"></p>
</form>
</body>
</html>
2. 使用Form组件实现注册功能校验数据
用户的注册功能:
用户输入密码,不能少于3位数字,如果少于3位,提示用户,密码不可以少于3位,并且密码不能为空
用户输入email邮件地址,不可为空,格式错误也不可以,都会提示
# views.py 文件
# 用Form组件实现用户注册功能,并完成数据校验
# 先定义一个类
from django import forms
class MyRegForm(forms.Form):
username = forms.CharField(label='用户名',
min_length=3,
error_messages={'required':'用户名不可以为空',
'initial':'xichen'},
initail='我是初始值'
)
password = forms.CharField(label='密码',
min_length=3,
error_messages={'required':'密码不能为空',
'min_length':'密码位数不得少于3位'}
)
email = forms.EmailField(error_messages={'required':'邮箱不能为空',
'invalid':'邮箱格式不正确,缺少@'})
# 在写一个视图函数
def rege(request):
form_obj = MyRegForm()
if request.method == 'POST':
form_obj = MyRegForm(request.POST)
if form_obj.is_valid():
return HttpResponse('没问题')
return render(request,'reg.html',locals())
// reg.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post" novalidate>
{% for foo in form_obj %}
<p>
{{ foo.label }}:{{ foo }} // 这里是前端接收后端传过来的数据后,进行模板渲染
<span style="color: red">{{ foo.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit" value="注册">
</form>
</body>
</html>
网页效果:查看网页效果发现,也验证了Form组件的功能
- 前端页面是form类的对象生成的-----------------------------------------生成HTML标签功能
- 当密码和email输入为空或错误之后,页面都会有提示 ------------用户提交校验功能
- 当用户密码和email输错之后,上次的内容还保留在input框------保留上次输入内容
三、Form组件如何校验数据
案例:注册功能
用户输入的用户名,不得小于3位,不得大于8位
用户输入的密码,不得小于3位,不得大于8位
用户输入的邮件地址必须符合格式要求
# views.py 文件
# 用Form组件实现用户注册功能,并完成数据校验
# 先定义一个类
from django import forms
class MyRegForm(forms.Form):
username = forms.CharField(min_length=3,
max_length=8,
)
password = forms.CharField(min_length=3,
max_length=8,
)
email = forms.EmailField() # 必须符合xxx@qq.com的格式,不用传参数
我们现在Python Console控制台中测试一下
1.先导入from app01 import views
2.给自定义的类传一个字典,并是实例化一个obj
obj = views.MyRegForm({'username':'xichen','password':'12','email':'123'})
3.判断数据是否全部合法
obj.is_valid() # 只有数据全部符合要求才返回True,否则False
4.这里明显返回了Flase,查看符合校验规则的数据用cleaned_data
obj.cleaned_data
返回:{'username':'xichen'} # 以字典的方式返回的
5.查看不符合条件的数据以及不符合的原因是什么
obj.errors
返回:{'password': ['Ensure this value has at least 3 characters (it has 2).'],
'email': ['Enter a valid email address.']}
# 以字典的形式返回,并且字典的key是我们的校验的字段,对应的value值是该字段的错误信息
注意1:少传一个数据会出现什么情况
# 少传一个email字段
obj = views.MyRegForm({'username':'xichen','password':'123'})
# 判断数据是否全部合法
obj.is_valid()
返回:Falase
# 查看一下副歌规范的数据
obj.cleaned_data
返回:{'username': 'xichen', 'password': '123'}
# 查看不符合规范的数据
obj.errors
返回:{'email': ['This field is required.']}
注意2:多传一个数据会发生什么情况
# 多传一个键值对会怎么样
obj = views.MyRegForm({'username':'xichen','password':'123','email':'123@qq.com','age:'18'})
# 判断数据是否全部合法
obj.is_valid()
返回:True
# 查看一下副歌规范的数据
obj.cleaned_data
返回:{'username': 'xichen', 'password': '123','email':'123@qq.com'}
# 查看不符合规范的数据
obj.errors
返回:{}
总结:
1.校验数据的时候 默认情况下类里面所有的字段都必须传值
2.默认情况下可以多传 但是绝对不能少传
四、Form组件如何渲染前端页面
forms组件只会帮你渲染获取用户输入(输入,选择,下拉框...)的标签 提交按钮需要你自己手动写
# views.py文件
def myform(request):
1.生成一个空的myRegForm对象
form_obj = MyRegForm()
return render(request,'reg.html',locals())
方式一:
不推荐使用:封装程度太高了 标签样式及参数不方便调整 可扩展性差
<p>
{{ form_obj.as_p }} //这里会自动将后端传来的自定义类的form_obj对象以p标签的像是展示
{{ form_obj.as_ul }}//这里会自动将后端传来的自定义类的form_obj对象以ul标签的像是展示
</p>
方式二:
不推荐使用:扩展性较高 不足之处在于 需要你手写的代码量比较多
// 自定义类MyRegForm有三个字段,三个输入框
// {{ form_obj.username }}是获取用户名的输入框
// {{ form_obj.username.label }}是获取用户名的标签,这里的标签由于我们没有自己给他传label参数,所以文本对应的就是username
<p>
{{ form_obj.username.label }}:{{ form_obj.username }}
</p>
<p>
{{ form_obj.password.label }}:{{ form_obj.password }}
</p>
<p>
{{ form_obj.email.label }}:{{ form_obj.email }}
</p>
方式三:
推荐使用:代码量和扩展性都很高
{% for foo in form_obj %} //循环这个后端传来的自定义类的对象
<p>{{ foo.label }}{{ foo }}</p> //{{foo}}对应方式2中{{ form_obj.username }}。。
{% endfor %}
五、Form组件如何展示错误信息
# view.py 文件
# 先后端,进行数据校验
def reg(request):
# 生成一个MyRegForm的空对象
form_obj = MyRegForm()
# 判断是否位POST请求
if request.method == 'POST':
# 向自定义类的对象传入数据进行校验,必须传字典。。。
# request.POST得到的数据就是一个字典,
form_obj = MyRegForm(request.POST)
if form_obj.is_valid():# 判断校验数据是否全部合法
return HttpResponse('用户注册成功')
return render(request,'reg.html',locals())
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% for foo in form_obj %}
<p>{{ foo.label }}:{{ foo }}</p>
<span style="color: red">{{ foo.errors}}</span>
{% endfor %}
<input type="submit" value="注册">
</form>
</body>
</html>
-
当我们什么也不配置时,去浏览器访问url注册用户时,我们发现前端会自动提示该输入框对应的一些约束,需要在form表单中添加一个参数
<form action="" method="post" novalidate>
-
在后端做校验时,必须将foo.error------foo.error.0
因为error返回的是一个列表,索引去第0个即可
提示:由于我们错误提示信息默认是英文的,我们只要这么做就可以了
配置字段信息的error_messages参数,是一个字典的型式
# views.py 文件
from django import forms
class MyRegForm(forms.Form):
username = forms.CharField(min_length=3,
max_length=8,
error_messages={
'min_length':'用户名最短三位',
'max_length':'用户名最长八位',
'required':'用户名不能为空'
})
password = forms.CharField(min_length=3,
max_length=8,
error_messages={
'min_length':'密码最短三位',
'max_length':'密码最长八位',
'required':'密码不能为空'
})
email = forms.EmailField(error_messages={
'required':'邮箱不能为空',
'invalid':'邮箱格式不正确'})
六、Form组件 参数
# 以下都是字段的参数
1.label:可以将label标签设置为中文
2.required:代表某一个输入框是否可以为空
required='可以写不为空的错误信息'
required=False 代表当前的输入框的内容可以为空
3.initial:某个输入开给你的初始值
initial='初始值'
4.error_messages:数据规则的错误信息
error_messages={}
5.invalid:提示邮箱的数据格式错误信息
6.password:将输入框改为密文的
widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
widget=forms.widgets.TextInput(attrs={'class': 'c1'}, render_value=True)
# 上面的attrs是添加类属性,如果多个属性用空格隔开
7.每个字段 还支持正则校验
from django import forms
from django.forms import Form
from django.core.validators import RegexValidator
class MyForm(Form):
user = forms.CharField(
phone=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],) # 可以放对各正则表达式
8.radioSelect
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
)
pwd = forms.CharField(min_length=6, label="密码")
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女")),
label="性别",
initial=2,
widget=forms.widgets.RadioSelect()
)
9.单选Select
class LoginForm(forms.Form):
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
10.多选Select
class LoginForm(forms.Form):
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
11.单选checkbox
class LoginForm(forms.Form):
...
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
12.多选checkbox
class LoginForm(forms.Form):
...
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
七、Form组件 钩子函数
针对字段 你还可以做额外的校验 需要通过钩子函数
1. 局部钩子函数
当你需要对某一个字段数据进行额外的一些列校验 你可以考虑使用钩子函数
针对单个字段的 使用局部钩子
我们在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验。
# views.py 文件
from django import forms
class MyRegForm(forms.Form):
username = forms.CharField(min_length=3,
max_length=8,
error_messages={
'min_length':'用户名最短三位',
'max_length':'用户名最长八位',
'required':'用户名不能为空'
})
password = forms.CharField(min_length=3,
max_length=8,
error_messages={
'min_length':'密码最短三位',
'max_length':'密码最长八位',
'required':'密码不能为空'
})
email = forms.EmailField(error_messages={
'required':'邮箱不能为空',
'invalid':'邮箱格式不正确'})
# 在类中定义clean_username局部钩子函数,对这个username进行字段的额外校验
def clean_username(self):
username = self.cleaned_data.get('username')
if '哈哈' in username:
# 给username字段下面提示错误信息
self.add_error('username','用户名不能包含哈哈字符')
return username
2. 全局钩子函数
我们在Fom类中定义 clean() 方法,就能够实现对字段进行全局校验。
针对多个字段的校验 使用全局钩子
eg:校验两次密码是否一致
# views.py 文件
from django import forms
class MyRegForm(forms.Form):
username = forms.CharField(min_length=3,
label='用户名',
max_length = 8,
error_messages = {
'min_length': '用户名最短三位',
'max_length': '用户名最长八位',
'required': '用户名不能为空',
})
password = forms.CharField(min_length=3,
label="密码",
max_length = 8,
error_messages = {
'min_length': '密码最短三位',
'max_length': '密码最长八位',
'required': '密码不能为空'
})
re_password = forms.CharField(max_length=8,
min_length=3,
label="确认密码",
error_messages={
'min_length': '密码最短三位',
'max_length': '密码最长八位',
'required': '密码不能为空'
})
email = forms.EmailField(label='邮件',
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式不正确'})
# 在类中定义clean方法,用来校验密码和确认密码字段是否相同
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if not password == re_password:
self.add_error('re_password', '两次密码不一致')
return self.cleaned_data