forms组件
当我们比如在注册页面的时候,获取用户输入的用户名、密码或邮箱是否符合我们设置的规则,比如设置密码长度,当不足的时候会提示信息
def register(request): # 定义错误信息空字典 error_dict = {'username': '', 'password': ''} if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if '666' in username: error_dict['username'] = '光喊666是不行的!!!' if not password: error_dict['password'] = '密码不能为空!!!' return render(request, 'register.html', locals())
前端代码:
<form action="" method="post"> <p>用户名: <input type="text" name="username"> <span style="color: red">{{ error_dict.username }}</span> </p> <p>密码: <input type="text" name="password"> <span style="color: red">{{ error_dict.password }}</span> </p> <input type="submit"> </form>
以上我们做了:
1、手写获取用户输入的前端页面代码 >>> 渲染页面
2、后端获取用户数据并做合法校验 >>> 校验数据
3、将校验之后的结果渲染到前端页面 >>> 展示信息
我们可以通过forms组件来帮我们做这些事
总结一下,其实form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
1、先写一个类
from django import forms class MyForms(forms.Form): # 用户名最大8位,最少2位,错误提示信息,允许为空,默认值 username = forms.CharField(max_length=8, min_length=2, error_messages={ 'max_length': '用户名最长8位', 'min_length': '用户名最短2位', }, required=False, initial='shen') # 密码必须最少3位最多8位,且标签名是密码,控制type属性为password password = forms.CharField(max_length=8, min_length=3, label='密码:', widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})) # 必须邮箱格式 email = forms.EmailField(error_messages={ 'required': '邮箱必填', 'invalid': '邮箱格式不正确' }) # 引用模块validators中RexValidator进行正则校验 phone = forms.CharField( validators=[ RegexValidator(r'^[0-9]+$', '请输入数字') ] )
常用的参数:
label input的提示信息 error_messages 自定义报错的提示信息 required 设置字段是否允许为空,允许为空改为False initial 设置默认值 widget 控制type类型及属性 widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2'}) widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}) validators 正则校验 validators=[ RegexValidator(r'^[0-9]+$', '请输入数字') ]
其他字段及参数
# 单选 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() )
2、后端校验数据
def reg(request): # 先生成一个空的类的对象 form_obj = MyForms() if request.method == 'POST': # 获取用户数据并交给forms组件校验 request.POST # 此处form_obj与上面定义的空对象要相同 form_obj = MyForms(request.POST) # 获取校验结果 if form_obj.is_valid(): return HttpResponse('数据没问题') else: # 获取校验失败的字段和提示信息 print(form_obj.errors) # 直接将该对象传给前端页面 return render(request, 'reg.html', locals())
校验数据的方法:
1、将数据字典传入我们写的类中
form_obj = MyForms({'username':'shen', 'password': '11', 'email': '111', 'phone': '1234'})
2、判断数据是否符合校验规则
form_obj.is_valid() # 该方法得到结果只有True和False,只有全部通过校验才会返回True
3、获取哪些数据是校验通过的
form_obj.cleaned_data
4、获取哪些数据是校验失败的,和失败原因
form_obj.errors
5、注意:forms组件所有的字段都必须要传参,不能少了,但是传多了没事,多传的会自动忽略
form_obj = views.MyRegForm({'username':'jason','password':'123456','phone':'11213'}) form_obj.is_valid() # False form_obj.errors # {'email': ['This field is required.']} form_obj=views.MyRegForm({'username':'jason','password':'123456',"email":'123@qq.com',"hobby":'hahahaha'}) form_obj.is_valid() # True form_obj.cleaned_data # {'username': 'jason', 'password': '123456', 'email': '123@qq.com'} form_obj.errors # {}
3、在前端渲染页面
校验数据前后端都应该要有的,但是前端的校验弱不禁风容易被修改,所以后端的校验必须要非常全面
如何取消前端浏览器帮我们做的自动校验功能:利用form表单中的 novalidate
<form action="" method="post" novalidate>
form组件会帮你在前端渲染用户的输入(选择,输入,下拉,文件)的标签,不会渲染按钮,并且渲染出的默认提示信息都是类中的字段首字母大写
三种渲染方式:
{#第一种渲染方式 本地测试方便,封装程度很高,扩展性低#} <form action="" method="post"> {{ form_obj.as_p }} 每一个input框都是p标签 {{ form_obj.as_table }} 按照表格渲染一行展示 {{ form_obj.as_ul }} 按照无序列表渲染展示 <input type="submit"> </form>
{#第二种渲染方式 扩展性高,但是书写太繁琐#} <form action="" method="post"> {# 给username添加label标签和id#} <label for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</label> {{ form_obj.username }} {# 获取标签的展示信息 获取标签input框#} {{ form_obj.password.label }} {{ form_obj.password }} {{ form_obj.email.label }}: {{ form_obj.email }} {{ form_obj.phone.label }}: {{ form_obj.phone }} <input type="submit"> </form>
{#第三种渲染方式 推荐使用#} <form action="" method="post" novalidate> {# 循环取出没一个字段#} {% for form in form_obj %} <p> {# 字段展示提示信息 字段input框#} {{ form.label }} {{ form }} {# 渲染错误提示信息#} <span>{{ form.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form>
4、钩子函数
实现自定义的校验功能,在自定义类下使用钩子函数
1、全局钩子
在form类中使用 clean() 方法,实现同时对多个字段进行操作
# 定义全局钩子来校验两次密码是否相同 # 定义全局钩子,必须是clean def clean(self): # 获取字段对应的数据 password = self.cleaned_data.get('password') repassword = self.cleaned_data.get('repassword') if not password == repassword: # 添加错误提示到repassword字段中的错误信息中 self.add_error('repassword', '两次密码不一样!!!') # 返回拿到的全局数据 return self.cleaned_data
2、局部钩子
在form类中使用 clean_字段名() 方法,实现对某一个字段的校验
# 定义局部钩子校验用户名中不能有GG def clean_username(self): # 获取局部定义钩子操作的字段数据 username = self.cleaned_data.get('username') if 'GG' in username: # 添加错误信息到username错误信息中 self.add_error('username', 'GG是不行的') # 返回拿到的局部钩子字段 return username
总结:如果想同时操作多个字段的数据就用全局钩子,如果想操作单个字段的数据,就用局部钩子