form组件能做的事:
1 渲染html代码
2 校验数据
3 展示提示信息
校验数据
from app01 import models
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.']}
# 5 校验数据只校验类中出现的字段,多传不影响,多传的字段直接忽略。 (多传)
form_obj = views.MyForm({'username':'jason','password':'123','email':'123@q.com','hobby':'study'})
form_obj.is_valid()
True
# 6 校验数据,默认情况下,类里面所有的字段都必须传值 (少传)
form_obj = views.MyForm({'username':'jason','password':'123'})
form_obj.is_valid()
False
form_obj.errors
{'email': ['This field is required.']}
校验数据的时候,默认情况下,数据可以多传但是绝不可能少传。
form_obj = views.MyForm({'username':'jason','email':'123'}) #创建froms对象
form_obj.cleaned_data # 查看校验通过的数据
form_obj.errors # 查看错误的数据
form_obj.is_valid() # 判断数据是否合法。
渲染标签
# form组件渲染html标签
def index(request):
# 1 先产生一个空对象
form_obj = MyForm()
# 2 直接将空对象传递给html页面
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 %} '遍历对象内的属性,使用for循环依次渲染'
"""
label 属性默认展示的是类中定义的字段首字母大写的形式
也可以自己修改,直接给字段对象加label属性即可。
username = forms.CharField(min_length=3,max_length=8,label='用户名')
"""
展示提示信息(可自定义)
如何让浏览器不自动校验? 在form表的属性中加 novalidate(无校验)
<form action="" method="post" novalidate>
{% 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组件渲染html标签
def index(request):
# 1 先产生一个空对象
form_obj = MyForm() # 这里
if request.method == 'POST':
pass
# 获取用户数据并校验
"""
1 获取数据繁琐
2 校验数据需要构造成字段格式传入才行
ps:但是request.POST可以看成是一个字典
"""
# 3 校验数据
*form_obj = MyForm(request.POST) # 这里
# 4 判断数据是否合法
if form_obj.is_valid():
# 5 如果合法,操作数据库存储数据
return HttpResponse('ok')
# 5 不合法有错误
# # 如何将错误信息展示到前端。
# 2 直接将空对象传递给html页面
return render(request, 'index.html', locals())
"""
1 必备条件 get请求和post传给html页面对象变量名必须一样(这里)
2 froms组件当你的数据不合法的情况下, 会保存上次的数据,让你基于之前的结果进行修改,更加的人性化
"""
针对错误的提示信息,可以自己定制
class MyForm(forms.Form):
# username 字符串类型最小3位,最大8位
username = forms.CharField(min_length=3, max_length=8, label='用户名',
error_messages={
'min_length':'用户名最少3位',
'max_length':'用户名最多8位',
'required':'用户名不能位空'
})
# password 字符串类型最小3位,最大8位
password = forms.CharField(min_length=3, max_length=8, label='密码',
error_messages={
'min_length': '密码最少3位',
'max_length': '密码最多8位',
'required': '密码不能位空'
}
)
# email字段必须符合邮箱格式 xxxx@xxx.com
email = forms.EmailField(label='邮箱',
error_messages={
'invalid':'邮箱格式不正确',
'required': '邮箱不能位空'
}
)
钩子函数
钩子函数在forms组件中,就类似于第二道关卡,除了forms组件自带的校验规则,还可以自定义校验规则。
局部钩子和全局钩子
局部钩子:
用在给单个字段增加校验规则时候,例如:用户名中不能含有某个字母
全局钩子:
用在需要给多个字段增加校验规则的时候,例如:校验密码和再次输入密码,两个值是否相同
from django import forms
from app01 import models
class MyRegForm(forms.Form): '钩子函数直接写在forms类中即可'
username = forms.CharField(label='用户名', min_length=3, max_length=8,
error_messages={
'required': '用户名不能为空',
'min_length': '用户名最少三位',
'max_length': '用户名最大八位',
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
)
password = forms.CharField(label='密码', min_length=3, max_length=8,
error_messages={
'required': '密码不能为空',
'min_length': '密码最少三位',
'max_length': '密码最大八位',
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
)
confirm_password = forms.CharField(label='确认密码', min_length=3, max_length=8,
error_messages={
'required': '确认密码不能为空',
'min_length': '确认密码最少三位',
'max_length': '确认密码最大八位',
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
)
email = forms.EmailField(label='邮箱',
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式不正确',
},
widget=forms.widgets.EmailInput(attrs={'class': 'form-control'})
)
# 1 局部钩子
def clean_username(self): # clean_username // clean_ 有几个字段就出几个
# 获取用户名
username = self.cleaned_data.get('username')
if '666' in username:
# 提示前端展示错误信息
self.add_error('username', '666不允许存在')
# 将钩子函数钩取出来的数据再放回去
return username
# 2全局钩子
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
# 钩子函数,在类里面书写方法即可
forms组件其他参数及补充
lable 字段名 给字段起名字
error_messages 自定义报错信息
initial 默认值
required=False 控制字段是否必填
max_length 最大位数
min_length 最小位数
widget 控制标签样式,类,属性
validators 让数据校验支持正则
针对不同类型的input 如何修改
text
password
date
radio
checkbox
"""
widget=forms.widgets.TextInput(attrs={'class':'form-control','username':'jason'})
# 写成字典格式,可以传单个属性,也可以自定义属性
widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})
# 多个属性值的话直接空格隔开即可,
# PasswordInput 控制type 类型的
# attrs={'class':'form-control c1 c2'} 控制属性的
RegexValidator验证器(使数据校验支持)
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
)
其他类型渲染
'写在forms类中即可 然后前端通过for循环,会一起渲染到前端页面'
# radio
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
# select
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=1, # 默认值
widget=forms.widgets.Select()
)
# 多选select
hobby2 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'})
)
# 单选checkbox
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput(),
)
# 多选 checkbox
hobby3 = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
forms数据校验总结
lable 字段名 给字段起名字
error_messages 自定义报错信息
initial 默认值
required=False 控制字段是否必填
max_length 最大位数
min_length 最小位数
widget 控制标签样式,类,属性
validators 让数据校验支持正则
"""
针对字段的校验有很多种
1 最简单的 min_length
2 正则 validators
3 钩子函数
前端的校验可有可无,但是后端的校验一点都不能含糊
form表单如何取消浏览器自动校验功能
在form表的属性中加 novalidate(无校验)
<form action="" method="post" novalidate></from>
forms组件源码
"""
切入点:
form_obj.is_valid()
"""
···············································
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return self.is_bound and not self.errors
# 如果is_valid要返回true的话 self.is_bound 要为true , self.errors 要为false
self.is_bound = data is not None or files is not None # 只要传值了is_bound肯定为true
········································
@property
def errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors
················································
def full_clean(self):
"""
Cleans all of self.data and populates self._errors and
self.cleaned_data.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields() # 校验字段 + 自动调用局部钩子
self._clean_form() # 全局钩子需要一个返回值,cleaned_data
self._post_clean()
' forms组件的主要方法都来自上面三个方法'
····························································
def _clean_fields(self):
for name, field in self.fields.items(): # 循环获取字段名和字段对象
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) # 获取字段对应的用户数据
self.cleaned_data[name] = value # 将合法的字段添加到cleaned_data
if hasattr(self, 'clean_%s' % name): # 利用反射获取局部钩子函数
value = getattr(self, 'clean_%s' % name)() #局部钩子需要有返回值
self.cleaned_data[name] = value
try:
xxxxxx
except ValidationError as e:
self.add_error(name, e)
总结: self._clean_fields() # 校验字段 + 自动调用局部钩子
self._clean_form() # 全局钩子需要一个返回值,cleaned_data
主要用了这两个方法