Forms组件、勾子函数
Forms组件
前戏
前戏:编写用户登录功能并且校验数据返回提示信息(form表单)
写一个注册功能
1.获取用户名和密码 利用form表单提交数据
2.在后端判断用户名和密码是否符合一定的条件
3.用户名不能是Jason
4.密码不能为123
# 符合条件需要你将提示信息动态的展示到前端页面
前端
<form action="" method="post">
<p>username:
<input type="text" name="username">
{# 行内标签 get请求为空 不占任何标签 post请求有值 就可以点到对应的数据 #}
<span style="color: red">{{ data_dict.username }}</span>
</p>
<p>password:
<input type="text" name="password">
<span style="color: red">{{ data_dict.password }}</span>
</p>
<input type="submit" class="btn btn-info">
</form>
后端
def ab_form(request):
# 不能使用ajax,那么它就是影响的整个页面
data_dict = {'username': '', 'password': ''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason':
data_dict['username'] = 'jason是不能使用的'
if password == '123':
data_dict['password'] = '密码不能设置这么简单的'
return render(request, 'ab_form.html', locals())
form组件
1.数据校验
支持提前设置各种校验规则 之后自动校验
2.渲染页面
支持直接渲染获取用户数据的各种标签
3.展示信息
支持针对不同的校验失败展示不同的提示
form组件基本使用
1.form类型创建
首先需要导入模块:from django import forms
然后创建类
from django import forms
class MyForm(forms.Form):
# 用户名最长8个字符,最短3个字符
name = forms.CharField(max_length=8, min_length=3)
# 用户年龄最大150,最小0岁
age = forms.IntegerField(max_value=150, min_value=0)
# 邮箱必须符合邮箱格式
email = forms.EmailField()
2.校验数据
测试环境俩种方式
1.测试环境的准备 可以自己拷贝代码准备
2.其实在pycharm左下角已经帮你准备一个测试环境 "python console"
from app01 import views
# 1 将带校验的数据组织成字典的形式传入即可
form_obj = views.MyForm({'username':'jason','password':'123','email':'123'})
# 2 判断数据是否合法
注意该方法只有在所有的数据全部合法的情况下才会返回True
form_obj.is_valid()
False
# 3 查看所有校验通过的数据
form_obj.cleaned_data
{'username': 'jason', 'password': '123'}
# 4 查看所有不符合校验规则以及不符合的原因
form_obj.errors
{
'email': ['Enter a valid email address.']
}
校验数据只校验类中出现的字段 多穿不影响 多穿的字段直接忽略
form_obj = views.MyForm({'username':'jason','password':'123','email':'123@qq.com','hobby':'study'})
form_obj.is_valid()
True
校验数据 默认情况下 类里面所有的字段都必须传值
form_obj = views.MyForm({'username':'jason','password':'123'})
form_obj.is_valid()
False
也就意味着校验数据的时候 默认情况下数据可以多传但是绝不可能少传
3.渲染标签功能(类中以外的所有标签都不会自动渲染 需要自己编写)
1.方式1(封装程度高 扩展性差)
{{ form_obj.as_p }}
{{ form_obj.as_table }}
{{ form_obj.as_ul }}
这种方法的缺点是封装程度太高了,什么都是人家渲染出来的,并且带了label和p标签,如果不想要呢,扩展性差
渲染成无序列表形式
2.方式2(封装程度低 扩展性好 编写困难)
{{ form_obj.name.label }}
{{ form_obj.name }}
{{ form_obj.age.label }}
{{ form_obj.age }}
{{ form_obj.email.label }}
{{ form_obj.email }}
3.方式3(推荐使用)
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}</p>
{% endfor %}
只渲染类里写的字段,如果要使用按钮之类的需要自己写,类中意外的字段都不会自动渲染
4.展示提示信息
1.form表单如何取消浏览器自动添加的数据校验功能
<form action="" method="post" novalidate>
{# novalidate不做任何校验#}
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}</p>
<input type="submit" value="提交">
{% endfor %}
</form>
2.form.errors生成的是列表的格式
3.错误信息展示
def func(request):
# 产生一个空对象
form_obj = MyForm()
if request.method == 'POST':
# 获取用户上传的数据,现在我们不需要用我们以前的那种笨拙的办法了(get)
# request.POST它是一个dict,将它看作是一个字典
# 产生了一个新的对象
form_obj = MyForm(request.POST)
# 判断一下我们传入的是否都是正确的
if form_obj.is_valid():
print(form_obj.cleaned_data)
return render(request, 'func.html', locals())
<form action="" method="post" novalidate>
{# novalidate不做任何校验#}
{% for form in form_obj %}
<p>
{{ form.label }}{{ form }}
<span style="color: red">{{ form.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit" value="提交">
</form>
4.错误信息修改为中文
name = forms.CharField(max_length=8, min_length=3, label='用户名',
error_messages={
'max_length':'用户名最多只能是8个字符',
'min_length':'用户名最少只能是3个字符',
'required':'用户名不能为空'
}
)
可以以直接修改这里,将整个底层结构展示为中文,在settings.py里
5.form中重要的字段参数
max_length、min_length
max_value、min_value
label 字段注释
error_messages 错误提示
required 是否为空
widget 标签类型、标签属性
initial 默认值
validators 正则校验
钩子函数
需求:以上我们写的form组件中写一个注册功能,现在要求判断用户是否已经存在
提供自定义的校验方式
局部钩子:校验单个字段
全局钩子:校验多个字段
1.局部钩子:校验单个字段
class MyForm(forms.Form):
'''属于第一层校验'''
# 用户名最长8个字符,最短3个字符
name = forms.CharField(max_length=8, min_length=3, label='用户名')
pwd = forms.IntegerField(label='密码')
confirm_pwd = forms.IntegerField(label='确认密码')
# 用户年龄最大150,最小0岁
age = forms.IntegerField()
# 邮箱必须符合邮箱格式
email = forms.EmailField()
'''属于第二层校验,第一层校验通过以后才会执行这层校验'''
# 1.校验用户名是否已经存在
def clean_name(self):
# 勾子函数会等上面的校验通过以后才会执行,相当于最后一层校验
# 第二层校验的话cleaned_data里面肯定会有值,因为第一层校验已经通过了
name = self.cleaned_data.get('name')
res = models.User.objects.filter(name=name).first()
if res:
# 给前端一个消息,这个消息鬼属于name框,出错了提示会放在name框中
return self.add_error('name', '用户名已存在')
# 没有问题的话,记得拿了什么东西要返回回去,“把人家的东西勾来了,记得给放回去”
return name
def func(request):
# 产生一个空对象
form_obj = MyForm()
if request.method == 'POST':
# 获取用户上传的数据,现在我们不需要用我们以前的那种笨拙的办法了(get)
# request.POST它是一个dict,将它看作是一个字典
# 产生了一个新的对象
form_obj = MyForm(request.POST)
# 判断一下我们传入的是否都是正确的
if form_obj.is_valid():
# 保存数据信息
res = form_obj.cleaned_data
# 要把确认密码移除调,它不属于我们数据库中,只是在校验中使用过
res.pop('confirm_owd')
models.User.objects.create(**res)
return render(request, 'func.html', locals())
2.全局钩子:校验多个字段
# 2.校验俩次密码是否一致
# clean这个字段就是校验多个字段的,固定的
def clean(self):
pwd = self.cleaned_data.get('pwd')
confirm_pwd = self.cleaned_data.get('confirm_pwd')
if not pwd == confirm_pwd:
return self.add_error('confirm_pwd', '两次密码不一致')
return self.cleaned_data
modelfrom组件
modelform是form的优化版本 使用更简单 功能更强大
class MyModelForm(forms.ModelForm):
class Meta:
# 写一下要争对哪一个表做校验
model = models.User
# 双下all代表着所有字段
fields = '__all__'
def clean_name(self):
name = self.cleaned_data.get('name')
res = models.User.objects.filter(name=name).first()
if res:
self.add_error('name','用户名已存在')
return name