前言
之前在HTML页面中利用form表单向后端提交数据时会写一些获取用户输入的标签并且使用form标签将其包裹起来。并且很多场景下都需要对用户的输入做校验,比如用户输入的长度和格式等,如果用户输入的有误就需要在页面上相应的位置显示相应的错误信息。而django form组件实现了上面所述的功能。
form组件功能
总结起来,django from组件的主要功能有以下三点:
①生成页面可用的HTML标签;
②对用户提交的数据进行校验;
③保留上次输入的内容。
form组件基本使用
form组件的使用和django模型非常类似,现在应用下的views.py
中定义好form
类,代码如下:
# views.py
from django import forms
class MyForm(forms.Form):
'''
# username字符串类型最小3位最大8位
# password字符串类型最小3位最大8位
# email字段必须符合邮箱格式 xxx@xx.com
# label属性用来指定字段展示信息
'''
# username字符串类型,最小3位,最大8位
username = forms.CharField(max_length=8,min_length=3, lable='用户名')
# password,字符串类型,最小3位,最大8位
password = forms.CharField(max_length=8,min_length=3, lable='密码')
# email字段必须符合邮箱格式
email = forms.EmailField(lable='邮箱')
测试form组件的环境也有两种方式,一种方式同样是通过test.py
,另一种方式就是通过pycharm的测试环境Python console
,这里采用第二种方式进行测试。
# 1 导入需要测试的文件
from first import views
# 2 将需要校验的数据组织成字典的形式传入自定义的form类进行校验
form_obj = views.MyForm({'username':'python',,'emali':'123'})
# 3 判断数据是否合法:注意该方法只有在所有的数据全部合法的情况下才会返回True
form_obj.is_valid() # False
# 4 查看所有通过校验的数据
form_obj.cleaned_data # {'username': 'python', }
# 5 查看所有不符合校验规则的字段和不符合原因,错误原因是列表,因为可能错误原因有多个
form_obj.errors # {'email': ['Enter a valid email address.']}
# 6 如果校验数据中出现了类中没有的字段,forms组件只会校验存在的字段,不存在的字段传了直接忽略
form_obj = views.MyForm({'username':'python',,'emali':'123@qq.com','hobby':'read'})
form_obj.is_valid() # True
# 7 默认情况下,类里面所有的字段都必须传值,也就意味着校验数据的时候,默认情况下数据可以多传但是不能少传
form_obj = views.MyForm({'username':'jason',})
form_obj.is_valid() # False
form组件的校验规则可以总结出以下方法:
给MyForm传值实例化对象,传值方式:将带校验的字段和数据组织成字典的形式
is_valid() 该方法只有在所有的数据全部合法的情况下才会返回True
cleaned_data 查看所有校验通过的数据
errors 查看所有不符合校验规则以及不符合的原因,{key: ['']}
has_error() 判断某一个字段是否不合法,不合法返回True
校验数据只校验类中出现的字段,多传不影响,多传的字段直接忽略
校验数据 默认情况下 类里面所有的字段都必须传值,即少传肯定不合法
form组件渲染标签
上面提到form组件可以自动帮忙渲染获取用户输入的标签(input select radio checkbox),但是不能自动渲染提交按钮,那么是如何实现的呢?首先是后端的代码,在views.py
中书写视图函数:
# views.py
def index(request):
# 先产生一个空对象
form_obj = MyForm()
if request.method == 'POST':
# 获取用户数据进行校验,如果一条一条的获取用户数据太过繁琐,而且校验数据需要字典格式,而request.POST获取到的数据格式就是queryset——dict对象
form_obj = MyForm(request.POST)
# 如果数据合法应该操作数据库,这里先简写
if form_obj.is_valid():
return HttpResponse('OK')
# 如果是get请求返回直接将空对象传递给html页面
return render(request,'register.html',locals())
如果数据不合法需要将错误信息展示到前端,那么如何将错误信息展示到前端呢?因为form校验产生的错误信息就是字典格式,到前端就是自定义对象,所以可以通过前端点点点的方式,下面是前端register.html页面
。
<body>
<form action="" method="post" novalidate> <!--novalidate参数阻止浏览器自动校验-->
{% for form in form_obj %}
<p>
{{ form.label }} : {{ form }}
<!--span是预留的展示错误信息的标签,form.errors.0的意思是只展示第一条错误提示,否则forms组件会将错误信息展示成无序列表的形式-->
<span style="color: red">{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
</form>
</body>
错误信息在前端显示都是英文的,能不能改成中文的呢?可以通过参数error_messages
来自定制错误信息。
class MyForm(forms.Form):
'''
error_messages参数需要构造成字典的数据结构,key是校验条件,value是校验失败时的提示信息
'''
username = forms.CharField(min_length=5,
max_length=12,
label='用户名',
error_messages={
'min_length': '用户名不能少于5位',
'max_length': '用户名不能超多12位',
'required': '用户名不能为空',
}
)
password = forms.CharField(min_length=6, max_length=12, label='密码')
re_password = forms.CharField(min_length=6, max_length=12, label='确认密码')
email = forms.EmailField(label='邮箱')
HOOK钩子函数
上述form组件的基本使用只是对数据的第一层校验,很多时候对参数还需要一些特殊的校验规则,这时候就可以使用钩子函数,钩子函数在form组件中类似于第二道校验,能够让我们自定义校验规则,校验流程通过第一层校验之后就会来到钩子函数,可以在钩子函数中进一步定制校验规则,钩子函数分为两种分别是局部钩子和全局钩子,钩子函数定义在form
类中。
局部钩子
当需要给单个字段增加校验规则的时候就可以使用局部钩子了。比如规定用户名中不能包含hook
。
# views.py
class MyForm(forms.Form):
username = forms.CharField(min_length=5,
max_length=12,
label='用户名',
error_messages={
'min_length':'用户名不能少于5位',
'max_length':'用户名不能超多12位',
'required':'用户名不能为空',
})
def clean_username(self):
username = self.cleaned_data.get('username')
if '666' in username:
self.add_error('username', '中包含了hook')
return username
定义局部钩子函数可以总结为以下几点:
①局部钩子的函数名为:clean_字段()
,字段就是自定义校验规则的字段;
②该方法中在cleaned_data
中获取该字段的数据,局部钩子中只能拿到当前字段的数据;
③校验数据失败时,通过add_error
方法给字段添加错误信息;
④局部钩子取出的字段数据一定要返回出去。
全局钩子
当需要给多个字段增加校验规则的时候可以使用全局钩子。比如校验两次密码输入是否一致:
# views.py
class MyForm(forms.Form):
password = forms.CharField(min_length=6, max_length=12, label='密码')
re_password = forms.CharField(min_length=6, max_length=12, label='确认密码')
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if password != re_password:
self.add_error('re_password', '两次密码输入不一致')
return self.cleaned_data
定义全局钩子函数可以总结为以下几点:
①全局钩子可以在cleaned_data
内获得任意字段的数据;
②全局钩子取出的cleaned_data
一定要返回出去。
form组件参数介绍
首先是常用参数:
min_length 最少几位字符
max_length 最多几位字符
label 字段名
required 控制字段是否必填(默认required=True)
error_messages 自定义报错信息,字典的结构
initial 初始值,input框里面的初始值value
HTML页面上的标签可以修改属性,那么form组件是直接渲染的,如何修改标签的属性呢?就是通过widget
参数,通过该参数可以修改标签的属性。
class MyForm(forms.Form):
username = forms.CharField(min_length=5,
max_length=12,
widget=forms.widgets.TextInput(attrs={'class': 'form-control'}))
"""
总结:
widget=forms.widgets.TextInput()默认是TextInput(及input[type='text'])
widget=forms.widgets.PasswordInput()密码格式
widget=forms.widgets.EmailInput()邮箱格式
attrs提供字段的属性,可以是内置的也可以是自定义的;如有多个class:空格隔开即可。
"""
另外在第一道校验规则中,还支持正则校验,就是通过validators
参数进行正则校验:
from django import forms
from django.core.validators import RegexValidator
class MyForm(forms.Form):
phone = forms.CharField(label='手机号',
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
],)
"""
总结:
validators的值是一个列表,烈面可以更多个正则表校验条件
RegexValidator第一个参数是正则表校验条件,第二个是校验失败是的提示信息
"""
form组件其他标签渲染
form组件不止能对input
框进行渲染,也支持对radio select checkbox
标签进行渲染:
# radio单选
gender = forms.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=1,
widget=forms.widgets.RadioSelect()
)
# checkbox多选
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
# 下拉单选:即使添加attrs={'multiple': 'multiple'}任然为单选
hobby2 = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
# 下拉多选
hobby3 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好2",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
# 选择checkbox是否选择, initial空表示False, 只要有值就是True默认选中(任何值都可以)
keep = forms.ChoiceField(
choices=(('False', 0), ('True', 1)),
label="是否记住密码",
initial='',
widget=forms.widgets.CheckboxInput()
)
在使用choice
参数时如果choices
展示的数据是动态从数据库中获取的,还需要额外的设置:
class BookAddForm(forms.Form):
name = forms.CharField(label='图书名称',
error_messages={'required': '图书名称不能为空'},
widget=forms.widgets.TextInput())
price = forms.DecimalField(label='价格',
error_messages={'required': '图书价格不能为空'},
widget=forms.widgets.TextInput())
publish_date = forms.DateField(label='出版日期',
error_messages={'required': '出版日期不能为空'},
widget=forms.widgets.DateInput())
publish_id = forms.ChoiceField(label='出版社',
error_messages={'required':'出版社不能为空'},
widget=forms.widgets.Select())
author = forms.MultipleChoiceField(label='作者',
error_messages={'required':'作者不能为空'},
widget=forms.widgets.SelectMultiple())
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# choices字段的数据动态来自数据库
self.fields['publish_id'].choices = models.Publish.objects.values_list('pk', 'name')
self.fields['author'].choices = models.Author.objects.values_list('pk', 'name')
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!