Django之form组件自动校验数据
一、form介绍
我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。
与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
Django form组件就实现了上面所述的功能。
总结一下,其实form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
二、普通方式手写注册功能
views.py
# 注册
def register(request):
error_msg = ""
if request.method == "POST":
username = request.POST.get("name")
pwd = request.POST.get("pwd")
# 对注册信息做校验
if len(username) < 6:
# 用户长度小于6位
error_msg = "用户名长度不能小于6位"
else:
# 将用户名和密码存到数据库
return HttpResponse("注册成功")
return render(request, "register.html", {"error_msg": error_msg})
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/reg/" method="post">
{% csrf_token %}
<p>
用户名:
<input type="text" name="name">
</p>
<p>
密码:
<input type="password" name="pwd">
</p>
<p>
<input type="submit" value="注册">
<p style="color: red">{{ error_msg }}</p>
</p>
</form>
</body>
</html>
三、使用form组件实现注册功能
views.py
先定义好一个RegForm类:
from django import forms
# 按照Django form组件的要求自己写一个类
class RegForm(forms.Form):
name = forms.CharField(label="用户名")
pwd = forms.CharField(label="密码")
再写一个视图函数:
# 使用form组件实现注册方式
def register2(request):
form_obj = RegForm()
if request.method == "POST":
# 实例化form对象的时候,把post提交过来的数据直接传进去
form_obj = RegForm(request.POST)
# 调用form_obj校验数据的方法
if form_obj.is_valid():
return HttpResponse("注册成功")
return render(request, "register2.html", {"form_obj": form_obj})
register2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册2</title>
</head>
<body>
<form action="/reg2/" method="post" novalidate autocomplete="off">
{% csrf_token %}
<div>
<label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
{{ form_obj.name }} {{ form_obj.name.errors.0 }}
</div>
<div>
<label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
{{ form_obj.pwd }} {{ form_obj.pwd.errors.0 }}
</div>
<div>
<input type="submit" class="btn btn-success" value="注册">
</div>
</form>
</body>
</html>
看网页效果发现 也验证了form的功能:
• 前端页面是form类的对象生成的 –>生成HTML标签功能
• 当用户名和密码输入为空或输错之后 页面都会提示 –>用户提交校验功能
• 当用户输错之后 再次输入 上次的内容还保留在input框 –>保留上次输入内容
四、pycharm的专属测试环境
1.使用方法
1.导入要测试的py文件
2.生成一个对象
2.本地校验测试的一些参数
from_obj.is_valid() //判断校验是否通过
from_obj.cleaned_data //拿到当前符号校验的数据{'username': '111', 'password': '111'}
form_obj.errors //拿到当前校验不通过的数据
少传参数Flalse,多传Trun。因为少传了拿不到校验的数据,多传了也不会使用。这是字典的形式
五、html自动生成input用户输入框的三种方式
forms组件只帮你渲染获取用户输入(输入 选择 下拉 文件)的标签 不渲染按钮和form表单标签
渲染出来的每一个input提示信息都是类中字段首字母大写
class MyRegForm(forms.Form):
username = forms.CharField(max_length=8, min_length=3) # 最长8位,最短3位
password = forms.CharField(max_length=8, min_length=3) # 最长8位,最短3位
email = forms.EmailField() # 邮箱验证
def reg(request):
# 1 先生成一个空的类对象
form_obj = MyRegForm()
if request.method == 'POST':
# 3 获取用户数据并交给forms组件校验 request.POST
form_obj = MyRegForm(request.POST)
# 4 获取校验结果
if form_obj.is_valid():
return HttpResponse('数据没问题')
else:
# 5 获取校验失败的字段和提示信息
print(form_obj.errors)
# 2 直接将该对象传给前端页面
return render(request, 'reg.html', locals())
第一种方式 {{ form_obj.as_p }} (不推荐)
不推荐,封装程度高,扩展性低。一般测试本地用
<body>
<p>第一种渲染方式:多个p标签 本地测试方便 封装程度太高了 不便于扩展</p>
{{ form_obj.as_p }}
{#{{ form_obj.as_ul }}#}
{#{{ form_obj.as_table }}#}
</body>
第二种方式 {{ form_obj.username }} (不推荐)
不推荐,扩展性较高 ,书写较为繁琐,每一个input框都需要自己手动写
<p>第二种渲染方式: 扩展性较高 书写较为繁琐</p>
<label for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label>
{{ form_obj.username }}
{{ form_obj.password.label }}{{ form_obj.password }}
{{ form_obj.email.label }}{{ form_obj.email }}
第三种方式 for循环 (推荐)
推荐使用,for循环方式
{{ form.errors.0 }} # 这个是模板语法
<p>第三种渲染方式 推荐使用</p>
<form action="" method="post" novalidate>
{% for form in form_obj %}
<p>
{{ form.label }}{{ form }}
<span>{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit">
</form>
六、数据校验
*******数据校验一个前后端都得有 但是前端的校验弱不禁风 可有可无
而后端的校验则必须非常全面
如何取消浏览器自动帮我们校验的功能?
在form后加参数 no validate,不验证
form表单取消前端浏览器自动校验功能
<form action="" method="post" novalidate>
常用校验参数
max_length //允许输入的最大长度
min_length //最小长度
label input的提示信息,name、password等
error_messages 自定义报错的提示信息
'max_length':"用户名最长8位",
'min_length':"用户名最短3位",
'required':"用户名不能为空"
required 设置字段是否允许为空
initial 设置默认值
widget 控制type类型及属性
使用方式
CharField用户名类型校验
from django.forms import widgets //不导入也可以,没提示
class MyRegForm(forms.Form):
# 用户名最少3位最多8位
username = forms.CharField(max_length=8,min_length=3,label='用户名',
error_messages={ //报错信息的提示
'max_length':"用户名最长8位",
'min_length':"用户名最短3位",
'required':"用户名不能为空"
},required=False,initial='jeff', // 为空和默认值
widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'}), //设置input的type属性为text,及class属性
)
password校验
class LoginForm(forms.Form):
...
pwd = forms.CharField(
min_length=6,
label="密码",
widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
)
邮箱类型校验
class MyRegForm(forms.Form):
# email字段必须填写符合邮箱格式的数据
email = forms.EmailField(label='邮箱',error_messages={
'required':'邮箱必填',
'invalid':'邮箱格式不正确'
})
手机号类型校验
正则匹配
from django.core.validators import RegexValidator
class MyRegForm(forms.Form):
# 手机号
phone = forms.CharField(
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
]
)
性别校验
gender = forms.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
爱好单选 select校验
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=3,
widget=forms.widgets.Select() # 单选
)
爱好多选select 校验1
hobby1 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
爱好多选chekbox校验2
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
是否记住密码校验
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
七、钩子函数
# 如果你想同时操作多个字段的数据你就用全局钩子
# 如果你想操作单个字段的数据 你就用局部钩子
钩子勾回来处理完之后,还要还回去的
局部钩子
校验用户名中不能包含666
# 局部钩子
# 这里的self是自己类,MyRegForm
def clean_username(self):
username = self.cleaned_data.get('username')
if '666' in username:
self.add_error('username','光喊666是不行的')
return username
全局钩子
两次密码验证
class MyRegForm(forms.Form):
# 密码
password = forms.CharField(max_length=8,min_length=3,label='密码',
widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
)
# 密码验证
re_password = forms.CharField(max_length=8,min_length=3,label='确认密码',
widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
)
def clean(self):
# 这里的self是自己类,MyRegForm
# 校验密码和确认密码是否一致
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('confirm_password')
if not password == re_password:
# 展示提示信息
self.add_error('re_password','两次密码不一致')
return self.cleaned_data