form组件
【一】FROM介绍
【1】引入
- 现有一需求,在不使用ajax的前提下,使用form组件完成向后端发送数据,后端获取到用户名和密码,并且判断用户名和密码是否符合一定条件
- 如密码长度限制,以及用户名查重等等,如果不符合条件,就需要将相关的消息渲染到前端页面
- form组件就可以很好的实现以上需求
【2】功能介绍
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
【二】form表单实现需求
【1】思路
- 在书写前端页面的时候可以在输入框后面加一个span标签
- 后端函数内可以定义一个字典,在里面放一些反馈信息
- 后端拿到数据后做一些校验,并且将反馈信息加入到字典内
- 通过render函数将这个字典传到前端页面,在前端页面用模板语法渲染对应的返回消息
【2】代码
前端页面
<body>
<form action="" method="post">
<p>username:<input type="text" class="" name="username"><span style="color: red">{{ info_dict.username }}</span></p>
<p>password:<input type="text" class="" name="password"><span style="color: red">{{ info_dict.password }}</span></p>
<p><button type="submit" class="btn btn-info"> 登录</button></p>
</form>
</body>
后端逻辑
def ab_form(request):
'''
无论是post请求还是get请求,页面都能获取到这个字典,
get请求一定没有值,
post请求可能有值
'''
info_dict = {'username': '', 'password': ''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username != 'green':
info_dict['username'] = '用户名错误'
if password != '123123':
info_dict['password'] = '密码错误'
return render(request, 'ab_form.html', locals())
【3】为何不在前端校验数据
- 仔细想想可以发现,对于数据的校验也可以在前端用js完成,为什么要那么麻烦的想办法发到前端再进行校验呢
- 这是因为前端的校验存在的风险较大,可能会被别人通过爬虫程序直接绕过前端页面向后端提交数据
- 所以数据的校验在前端可有可无,而后端必须要有。
【三】Forms组件的使用
- Forms组件可以代替我们后端手动校验数据的过程
- 通过导入Form这个类实例化出来对象,再使用封装好的方法就可以快速校验数据
【1】实例化对象
from django import forms
class MyForm(forms.Form):
# username 最大八位最小三位
username = forms.CharField(max_length=10, min_length=3)
# password 最大八位最小三位
password = forms.CharField(max_length=10, min_length=3)
# 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField()
# 实例化对象,传入获取的数据
form_obj = MyForm({'username': 'green',
'password': '12',
'email': '123123@qq.com'
})
【2】常用方法
# 数据是否完全合法(is_valid)
form_obj.is_valid()
# 查看合法数据(changed_data) 展示符合校验规则的数据
form_obj.changed_data # ['username', 'password']
# 查看不合法的数据(errors)
form_obj.errors
【3】多传不影响,少传有影响
多传参数是否会报错?
form_obj = MyForm({'username': 'green',
'password': '123',
'email': '123123@qq.com',
'hobby': 'music'
})
res = form_obj.is_valid()
print(res) # True
# 多传不会报错
少传参数是否报错
form_obj = MyForm({'username': 'green',
'password': '123'
})
res = form_obj.is_valid()
print(res) # False
# 少传会报错
【四】forms组件渲染HTML页面
- 自己手动写html代码有时略显麻烦,form组件可以为我们快速渲染一个输入标签的页面如(input/select),但是它不会自动渲染提交按钮
- 首先需要在后端视图函数里面实例化一个forms对象,不需要填参数
from django import forms
class MyForm(forms.Form):
# username 最大八位最小三位
username = forms.CharField(max_length=10, min_length=3, label='用户名')
# password 最大八位最小三位
password = forms.CharField(max_length=10, min_length=3, label='密码')
# 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField(label='邮箱')
def ab_form(request):
form_obj = MyForm() # 实例化一个空对象
return render(request, 'ab_form.html', locals())
【1】渲染方式一
- 通过模板语法拿到后端视图函数传来的空对象,这个对象可以用
as_p
,as_ul
,as_table
等方法直接渲染出来输入框页面 - 封装程度很高,不利于自定义拓展,一般用于本地测试
<h1>封装程度高,不利于后续扩展,但是书写方便,一般用于本地测试</h1>
<h1>第一种渲染方式 as_p</h1>
{{ form_obj.as_p }}
【2】渲染方式二
- 通过空对象点字段名方法(
form_obj.username
)可以渲染出来该字段对应的输入框 - 通过空对象点字段名点lable(
form_obj.username.lable
)可以渲染出来对应字段内地lable属性,如果没有该属性会自动渲染该字段名的大写开头形式 - 该方法封装程度低,利于拓展,但是需要书写的代码太多,十分笨重
<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>
【3】渲染方式三
- 可以通过for循环form_obj对象,每一次循环都会拿到等价于
form_obj.username
的东西 - 该方法不仅书写的代码量少,还有便于拓展,十分推荐使用
{% for form in form_obj %}
<p>{{ form.label }}:{{ form }}</p>
{% endfor %}
【五】forms组件渲染错误信息
- 在没有使用forms组件之前,都是通过在后端提前定义一个信息字典,在前端的每一个输入框之后都加一个span标签
- 后端拿到前端发来的数据,并且进行校验,如果错误就把对应的信息加到字典里,这样一来前端在提交数据之后就能在span标签内渲染对应的信息了
- 可是这样十分麻烦,用来forms组件就变得十分便捷
【1】forms组件
后端视图函数
def ab_form(request):
form_obj = MyForm()
if request.method == 'POST':
# 如果是post请求 就将这个post请求的内容赋值给这个空对象
# 这样form组件内部就能自动校验数据是否合法
form_obj = MyForm(request.POST)
if form_obj.is_valid():
return HttpResponse('OK')
return render(request, 'ab_form.html', locals())
后端form类
class MyForm(forms.Form):
# username 最大八位最小三位
# error_messages通过这个参数可以自定义对应的错误消息
username = forms.CharField(max_length=10, min_length=3, label='用户名',
error_messages={
'max_length': '用户名最长为10位',
'min_length': '用户名最短为3位',
'required': '用户名不能为空'
})
# password 最大八位最小三位
password = forms.CharField(max_length=10, min_length=3, label='密码',
error_messages={
'max_length': '密码最长为10位',
'min_length': '密码最短为3位',
'required': '密码不能为空'
})
# 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField(label='邮箱',
error_messages={
'invalid': '邮箱格式不正确',
'required': '邮箱不能为空'
})
前端页面
- 这里form标签的novalidate属性是为了取消浏览器的数据校验
form.errors.0
可以拿到对应字段动态渲染的错误消息
<form action="" novalidate method="post">
{% for form in form_obj %}
<p>
{{ form.label }}:{{ form }}
<span style="color: red;">{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit" class="btn btn-info">
</form>
【六】钩子函数
- 钩子函数是用于对数据二次校验的,校验的内容可以有我们自己高度的自定义,
- 钩子函数是定义在form类里面的
- 钩子函数只有在第一层校验正确的情况下才会被调用
- 钩子 函数分为局部钩子和全局钩子,局部钩子适用于给某一个字段的数据进行校验,全局钩子适用于多个字段的校验
form类代码
class MyForm(forms.Form):
# username 最大八位最小三位
# error_messages通过这个参数可以自定义对应的错误消息
username = forms.CharField(max_length=10, min_length=3, label='用户名',
error_messages={
'max_length': '用户名最长为10位',
'min_length': '用户名最短为3位',
'required': '用户名不能为空'
})
# password 最大八位最小三位
password = forms.CharField(max_length=10, min_length=3, label='密码',
error_messages={
'max_length': '密码最长为10位',
'min_length': '密码最短为3位',
'required': '密码不能为空'
})
password_confirm = forms.CharField(max_length=10, min_length=3, label='密码',
error_messages={
'max_length': '密码最长为10位',
'min_length': '密码最短为3位',
'required': '密码不能为空'
})
# 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField(label='邮箱',
error_messages={
'invalid': '邮箱格式不正确',
'required': '邮箱不能为空'
})
【1】局部钩子
- 此时我想对用户名进行进一步限制,比如用户名不能含有666,
# 局部钩子需要重写clean_字段名 函数
def clean_username(self):
# 获取到用户名,因为到了这一步说明第一步校验是通过的,所以直接从正确的数据里面拿
username = self.cleaned_data.get('username')
# 进行自定义校验
if '666' in username:
# 通过add_error 添加反馈信息, 这个方法有两个参数,第一个参数是对应的字段,第二个参数是填入的错误消息
self.add_error('username','不能扣666')
# 最后需要将钩出来的数据返回回去
return username
【2】全局钩子
- 此时我加一个密码二次确认的过程
# 全局钩子就需要重写clean函数
def clean(self):
# 获取到对应的数据
password = self.clean_data.get('password')
password_confirm = self.cleaned_data.get('password_confirm')
# 写自定义校验逻辑
if password != password_confirm:
# 添加错误消息
self.add_error('password_confirm','两次密码不一致')
# 因为是全局钩子,就需要把所有的数据都返回回去
return self.clean_data
【七】forms组件其他参数以及补充知识点
label 字段名
error_messages 自定义错误信息
initial 默认值
required 控制字段是否必填 ,不是必填 required=False
# 给input框定义type样式
# 参数是一个字典,字典的键是属性名,值是属性值
# 样式如果要加多个,用空格隔开即可
widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'})
# 正则表达式
# 这是一段手机号码的正则表达式,使用前需要先导入模块
# 可以一次写多个正则
from django.core.validators import RegexValidator
phone = forms.CharField(
validators=[
RegexValidator(regex=r'^\+?1?\d{9,15}$', message="手机号格式不正确"),
]
)
【1】常用字段
# initial 初始值,input框里面的初始值。
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三" # 设置默认值
)
pwd = forms.CharField(min_length=6, label="密码")
# error_messages 重写错误信息。
class LoginForm(forms.Form):
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
)
# radioSelect 单radio值为字符串
class LoginForm(forms.Form):
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
# sekect 单选
class LoginForm(forms.Form):
...
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
#