Django form组件
手动实现一个注册功能
用户名不能含有'haha',密码不能小于3位数
<!--前端--> <form action="" method="post"> <p>username:<input type="text" name="username"> <span>{{ errors.username }}</span></p> <!--用于展示错误提示--> <p>password:<input type="text" name="password"> <span>{{ errors.password }}</span></p> <input type="submit"> </form>
# 后端 def login(request): # 定义一个报错信息字典,用于前端span标签错误提示 errors = {'username':'', 'password':''} if request.method == 'POST': # 获取到用户名合密码 username = request.POST.get('username') password = request.POST.get('password') # 判断用户名和密码规则 if 'haha' in username: errors['username'] = '用户名不规范' if len(password) < 3: errors['password'] = '密码不能小于位' return render(request, 'login.html', locals())
# 整个过程共三步: 1.前端页面搭建 >>> 渲染页面 2.数据传输到后端校验 >>> 校验数据 3.展示错误信息 >>> 展示信息
一.为什么用forms组件
在需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确,
如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.的场景下就可以用forms组件
Django form组件能直接完成上面的三步操作:
1.渲染前端页面
2.校验数据是否合法
3.展示错误信息
ps: 当你要实现上面这三步时就可以考虑使用forms组件
二.如何使用forms组件
<1> 写一个继承了forms.Form的类
from django import forms class LoginForm(forms.Form): username = forms.CharField(max_length=8,min_length=3) # 用户名最长八位最短三位 password = forms.CharField(max_length=8,min_length=5) # 密码最长八位最短五位 email = forms.EmailField() # email必须是邮箱格式
#ps: 类中的username,password...字段,渲染到前端就是获取用户输入的input标签
注意: 类中的字段,如 username,password 一定要与数据库中的字段名字相同
<2>基本使用
# 在Python Console校验 1.将需要校验的数据,以字典的方式传递给自定义的类,实例化产生对象 form_obj = views.LoginForm({'username':'xionger','password':'123','email':'123'}) 2.查看数据是否全部合法 form_obj.is_valid() >>> False # 只有所有的数据都符合要求 才会是True 3.查看错误的字段及原因 form_obj.errors # 返回的是字典{字段名:[错误信息],...} { 'password': ['Ensure this value has at least 5 characters (it has 3).'], 'email': ['Enter a valid email address.'] } # 注意: 对象.errors 拿到的是左右错误字段的字典 4.查看校验合格的字段数据 form_obj.cleaned_data # 返回的是字典{字段名:值,...} {'username': 'xionger'}
注意:
1.自定义类中所有的字段默认都是必须要传值的
2.可以额外传入类中没有定义的字段名,forms组件不会去校验,也就意味着多传一点关系没有
form_obj = views.LoginForm({'username':'xionger','password':'123456','email':'123@qq.com'}) form_obj.is_valid() True form_obj = views.LoginForm({'username':'xionger','password':'123456'}) form_obj.is_valid() False # 少一个字段 form_obj = views.LoginForm({'username':'xionger','password':'123456','email':'123@qq.com','hobby':'read'}) form_obj.is_valid() True # 多出的字段不进行校验
<3>渲染页面
三种方式
<p>第一种渲染页面的方式(封装程度太高 一般只用于本地测试 通常不适用)</p> {{ from_obj }} {{ 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 foo in form_obj %} <p>{{ foo.label }}{{ foo }}</p> {% endfor %} <!-- form_obj 是LoginForm类的对象 form_obj.username 是 字段forms.CharField类的对象 也就是获取用户输入的input标签 form_obj.username.label 是对象 form_obj.username 的属性 foo == form_obj.username foo.label == form_obj.username.label -->
注意:
1.forms组件在帮你渲染页面的时候,只会渲染获取用户输入的标签,提交按钮需要你手动添加
2.input框的label注释,不指定的情况下,默认用的类中字段的首字母大写
<4>展示错误信息
<!--前端--> {% for foo in form_obj %} <p>{{ foo.label }}{{ foo }} <span>{{ foo.errors.0 }}</span> <!--foo.errors是错误的字段列表信息 如:['Enter a valid email address.'] 通过索引0取出内容-->
</p>
{% endfor %}
# 后端 def reg(request): # 1 现生成一个空的自定义类的对象 form_obj = LoginForm() # 2 将该对象传递给前端页面 if request.method == 'POST': # 3 获取前端post请求提交过来的数据 # print(request.POST) # 由于request.POST其实也是一个字典,所以可以直接传给LoginForm form_obj = LoginForm(request.POST) # 4 校验数据 让forms组件帮你去校验 if form_obj.is_valid(): # 5 如果数据全部通过 应该写入数据库 pass # 6 如果不通过 前端展示错误信息 return render(request,'reg.html',locals())
<5>钩子函数(HOOK)
forms组件暴露给用户可以自定义的校验规则
用法:在自定义的form类中书写方法即可
(1)局部钩子 clean_字段名 :针对某一个字段做额外的校验
def clean_username(self): # 需要额外校验的字段 username = self.cleaned_data.get('username') if '666' in username: # 添加提示的错误信息 self.add_error('username', '用户名不能有666') # 将字段返回 return username
(2)全局钩子 clean : 针对多个字段做额外的校验
def clean(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') # 判断两次密码是否一致 if not password == confirm_password: self.add_error('confirm_password','两次密码不一致') # 与局部钩子不同,这里需要将 cleaned_data 返回 return self.cleaned_data
补充:
1.校验数据的时候可以前后端都校验,做一个双重的校验 但是前端的校验可有可无,而后端的校验则必须要有,因为前端的校验可以通过爬虫直接避开 前端取消浏览器校验功能: <!--form标签指定novalidate属性即可--> <form action="" method='post' novalidate></form>
2.forms组件其他字段及操作方式: required 是否必填 label 注释信息 error_messages 报错信息 initial 默认值 widget 控制标签属性和样式 widget=widgets.PasswordInput(attrs={'class':'form-control c1 c2'}) # 改成密文,添加样式
3.其他字段了解知识点(知道有这些对象 需要用到的时候 能够知道去哪找) # 单选的radio框 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框 hobby1 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.SelectMultiple() ) # 单选的checkbox keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.widgets.CheckboxInput() ) # 多选的checkbox hobby2 = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.widgets.CheckboxSelectMultiple() ) # 正则 phone = forms.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )