Django Form组件
1. Form基础
1. forms组件介绍
1. 注册功能,登录功能,前端需要校验(字段长度,邮箱是否合法。。。)
2. 前端校验可以没有,后端校验是必须的,使用传统方式 if判断写的很多
3. 借助于forms组件,可以快速实现字段的校验
from django.forms import Form
1. 示例
"""
写一个注册功能
获取用户名和密码 利用form表单提交数据
在后端判断用户名和密码是否符合一定的条件
用户名中不能含有金瓶梅
密码不能少于三位
如何符合条件需要你将提示信息展示到前端页面
"""
def ab_form(request):
back_dic = {'username':'','password':''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if '金瓶梅' in username:
back_dic['username'] = '不符合社会主义核心价值观'
if len(password) < 3:
back_dic['password'] = '不能太短 不好!'
"""
无论是post请求还是get请求
页面都能够获取到字典 只不过get请求来的时候 字典值都是空的
而post请求来之后 字典可能有值
"""
return render(request,'ab_form.html',locals())
<form action="" method="post">
<p>username:
<input type="text" name="username">
<span style="color: red">{{ back_dic.username }}</span>
</p>
<p>password:
<input type="text" name="password">
<span style="color: red">{{ back_dic.password }}</span>
</p>
<input type="submit" class="btn btn-info">
</form>
"""
1. 手动书写前端获取用户数据的html代码 渲染html代码
2. 后端对用户数据进行校验 校验数据
3. 对不符合要求的数据进行前端提示 展示提示信息
forms组件
能够完成的事情
1.渲染html代码
2.校验数据
3.展示提示信息
为什么数据校验非要去后端 不能在前端利用js直接完成呢?
数据校验前端可有可无
但是后端必须要有!
因为前端的校验是弱不禁风的 你可以直接修改
或者利用爬虫程序绕过前端页面直接朝后端提交数据
购物网站
选取了货物之后 会计算一个价格发送给后端 如果后端不做价格的校验
实际是获取到用户选择的所有商品的主键值
然后在后端查询出所有商品的价格 再次计算一遍
如果跟前端一致 那么完成支付如果不一致直接拒绝
"""
2. 基本使用
from django import forms
class MyForm(forms.Form):
username = forms.CharField(min_length=3,max_length=8)
password = forms.CharField(min_length=3,max_length=8)
email = forms.EmailField()
3. 校验数据
"""
1. 测试环境的准备 可以自己拷贝代码准备
2. 其实在pycharm里面已经帮你准备一个测试环境
python console
"""
from app01 import views
form_obj = views.MyForm({'username':'jason','password':'123','email':'123'})
form_obj.is_valid()
False
form_obj.cleaned_data
{'username': 'jason', 'password': '123'}
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
"""
也就意味着校验数据的时候 默认情况下数据可以多传但是绝不可能少传
"""
4. 渲染标签
"""
forms组件只会自动帮你渲染获取用户输入的标签(input select radio checkbox)
不能帮你渲染提交按钮
"""
def index(request):
form_obj = MyForm()
return render(request,'index.html',locals())
<p>第一种渲染方式:代码书写极少,封装程度太高 不便于后续的扩展 一般情况下只在本地测试使用</p>
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
<p>第二种渲染方式:可扩展性很强 但是需要书写的代码太多 一般情况下不用</p>
<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>
<p>第三种渲染方式(推荐使用):代码书写简单 并且扩展性也高</p>
{% for form in form_obj %}
<p>{{ form.label }}:{{ form }}</p>
{% endfor %}
"""
label属性默认展示的是类中定义的字段首字母大写的形式
也可以自己修改 直接给字段对象加label属性即可
username = forms.CharField(min_length=3,max_length=8,label='用户名')
"""
2. forms校验字段功能
class MyForm(forms.Form):
name = forms.CharField(required=False, max_length=32, min_length=3,label='用户名')
email = forms.EmailField(label='邮箱')
age=forms.IntegerField(max_value=200,min_value=0,label='年龄')
def register(request):
data={'name':'lqz','email':'33333@qq.com','age':900}
form=myforms.MyForm(data)
if form.is_valid():
print('校验通过')
print(form.cleaned_data)
else:
print(form.cleaned_data)
print('校验失败')
print(form.errors)
print(type(form.errors))
from django.forms.utils import ErrorDict
print(form.errors.as_json())
print(form.errors.as_data())
return HttpResponse('ok')
3. forms渲染模板功能
1. 视图
# 视图函数
def register(request):
if request.method=='GET':
form=myforms.MyForm()
return render(request,'register.html',{'form':form})
elif request.method=='POST':
# 数据校验
form=myforms.MyForm(request.POST)
if form.is_valid():
print('校验通过,存数据库')
else:
print(form.errors.as_data())
print('校验失败,返回错误')
return HttpResponse('ok')
2. 模板
# 模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<hr>
<h1>手动创建模板</h1>
<form action="" method="post">
<p>用户名:<input type="text" name="name"></p>
<p>邮箱:<input type="text" name="email"></p>
<p>年龄:<input type="text" name="age"></p>
<p><input type="submit" value="提交"></p>
</form>
<hr>
<h1>半自动渲染模板1</h1>
<form action="" method="post">
<p>用户名:{{ form.name }}</p>
<p>邮箱:{{ form.email }}</p>
<p>年龄:{{ form.age }}</p>
<p><input type="submit" value="提交"></p>
</form>
<h1>半自动渲染模板2(用的最多)</h1>
<form action="" method="post">
<p>{{ form.name.label }}--{{ form.name }}</p>
<p>{{ form.email.label }}---{{ form.email }}</p>
<p>{{ form.age.label }}---{{ form.age }}</p>
<p><input type="submit" value="提交"></p>
</form>
<h1>半自动渲染模板3(用的最多)</h1>
<form action="" method="post">
{% for foo in form %}
<p>{{ foo.label }} :{{ foo }}</p>
{% endfor %}
<p><input type="submit" value="提交"></p>
</form>
<h1>全自动(了解)</h1>
<form action="" method="post">
{# {{ form.as_ul }}#}
{{ form.as_p }}
{# <table>#}
{# {{ form.as_table }}#}
{# </table>#}
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
2. Form进阶
1. froms渲染错误信息
1. form对象.errors 字典
2. name对象.errors
"""
浏览器会自动帮你校验数据 但是前端的校验弱不禁风
如何让浏览器不做校验: 设置 novalidate
<form action="" method="post" novalidate>
"""
def register(request):
if request.method=='GET':
form=myforms.MyForm()
return render(request, 'register.html',{'form':form})
else:
"""
1. 数据获取繁琐
2. 校验数据需要构造成字典的格式传入才行
ps: 但是request.POST可以看成就是一个字典
"""
form=myforms.MyForm(request.POST)
if form.is_valid():
return redirect('http://www.baidu.com')
else:
return render(request, 'register.html',{'form':form})
<form action="" method="post" novalidate>
{% for foo in form %}
<div class="form-group">
<label for="">{{ foo.label }}</label>
{{ foo }}
<span class="text-danger pull-right">{{ foo.errors }}</span>
</div>
{% endfor %}
<div class="text-center">
<input type="submit" value="提交" class="btn btn-danger">
</div>
</form>
"""
1. 必备的条件 get请求和post传给html页面对象变量名必须一样
2. forms组件当你的数据不合法的情况下 会保存你上次的数据
让你基于之前的结果进行修改更加的人性化
"""
2. forms组件参数配置
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
error_messages={'min_length': '太短了小伙子'}
class MyForm(forms.Form):
name = forms.CharField(
required=False, max_length=32, min_length=3, label='用户名',
widget = widgets.TextInput(attrs={'class': 'form-control'}),
error_messages = {'min_length': '太短了小伙子'}
)
password = forms.CharField(
required=False, max_length=32, min_length=3, label='密码',
widget = widgets.PasswordInput(attrs={'class': 'form-control'}),
error_messages = {'min_length': '太短了小伙子'}
)
re_password = forms.CharField(
required=False, max_length=32, min_length=3, label='确认密码',
widget = widgets.PasswordInput(attrs={'class': 'form-control'}),
error_messages = {'min_length': '太短了小伙子'}
)
email = forms.EmailField(
label='邮箱', error_messages={'required': '小伙子,这个必填'},
widget = widgets.TextInput(attrs={'class': 'form-control'})
)
age = forms.IntegerField(
max_value=200, min_value=0, label='年龄',
widget = widgets.TextInput(attrs={'class': 'form-control'})
)
text = forms.CharField(
label='个人简介',
widget = widgets.Textarea(attrs={'class': 'form-control'})
)
date = forms.CharField(
label='出生日期',
widget = widgets.DateInput(attrs={'class': 'form-control'})
)
label 字段名
error_messages 自定义报错信息
initial 默认值
required 控制字段是否必填
"""
1. 字段没有样式
2. 针对不同类型的input如何修改
text
password
date
radio
checkbox
...
"""
widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
]
3. 定制错误提示信息
class MyForm(forms.Form):
username = forms.CharField(min_length=3,max_length=8,label='用户名',
error_messages={
'min_length':'用户名最少3位',
'max_length':'用户名最大8位',
'required':"用户名不能为空"
}
)
password = forms.CharField(min_length=3,max_length=8,label='密码',
error_messages={
'min_length': '密码最少3位',
'max_length': '密码最大8位',
'required': "密码不能为空"
}
)
email = forms.EmailField(label='邮箱',
error_messages={
'invalid':'邮箱格式不正确',
'required': "邮箱不能为空"
}
)
4. forms其他类型渲染
gender = forms.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
hobby1 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
5. 钩子函数(HOOK)
1. 简介
在特定的节点自动触发完成响应操作
钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则
1. 局部钩子
当你需要给单个字段增加校验规则的时候可以使用
2. 全局钩子
当你需要给多个字段增加校验规则的时候可以使用
1. 局部钩子
校验用户名中不能含有666 只是校验username字段
2. 全局钩子
校验密码和确认密码是否一致 校验password confirm两个字段
2. 局部钩子
1. 在自定义的Form类中写 clean_字段名
2. 取出字段的真正值 name=self.cleaned_data.get('name')
3. 判断自己的规则 如果判断失败 抛出异常 ValidationError
4. 如果通过 return name
def clean_name(self):
name = self.cleaned_data.get('name')
if name.startswith('sb'):
raise ValidationError('不能以sb开头')
else:
return name
3. 全局钩子
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if password == re_password:
return self.cleaned_data
else:
raise ValidationError('两次密码不一致')
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if not confirm_password == password:
self.add_error('confirm_password','两次密码不一致')
return self.cleaned_data