django forms组件
django forms组件
一、forms组件的作用
① 手写获取用户输入的前端页面代码——渲染页面
② 后端获取用户数据并做合法性校验——校验数据
③ 将检验之后的结果渲染到前端页面——展示信息
它可以做到html页面用户输入、然后提交发送到后端进行校验、再将检验之后的结果发送到前端展示的一个完整流程。
二、如何使用forms组件校验数据
首先,先自定义一个MyRegForm类。forms组件的使用方法和创建表模型的使用方法比较相似
后端views.py代码:
from django import forms # 导入forms组件 class MyRegForm(forms.Form): username = forms.CharField(max_length=8, min_length=3) # username长度最多为8位,最少为3位 password = forms.CharField(max_length=8, min_length=3) # email字段必须填写符合邮箱格式的数据 email = forms.EmailField()
1、测试单个py文件的两种方法
① 前面已经介绍过了,在tests.py文件中写测试代码
② 使用django的工作台Django Console(下文中的测试过程用django工作台来进行测试)
2、forms组件校验数据方法
Django Console:
# 1.传入待校验的数据,用自己写的类传入字典格式的待校验的数据 from app01 import views form_obj = views.MyRegForm({'username': 'tank', 'password': '12', 'email': '123456789'}) # 2.判断数据是否符合校验规则 form_obj.is_valid() # 该方法只有在所有的数据全部符合校验规则时候才会返回True # 3.获取校验之后通过的数据 form_obj.cleaned_data # 4.获取校验失败及失败的数据 form_obj.errors
注意:forms组件默认所有的字段都必须传值,也就意味着传少了肯定是报错,而传多了没有关系,只校验里面的字段,多传的直接忽略
Django Console
form_obj = views.MyRegForm({'username': 'tank', 'password': '123456', 'email': '123456@qq.com'}) form_obj.is_valid() form_obj = views.MyRegForm({'username': 'tank', 'password': '123456',}) form_obj.is_valid() form_obj = views.MyRegForm({'username': 'tank', 'password': '123456', 'email': '123456@qq.com', 'age': 45646546}) form_obj.is_valid()
三、如何使用forms组件渲染页面
在MyRegForm类的基础上写一个reg视图函数
后端views.py代码:
def reg(request): # 1 先生成一个空对象 form_obj = MyRegForm() if request.method == 'POST': # 3 获取用户属性并交给forms组件进行校验 form_obj = MyRegForm(request.POST) # 2 直接将该对象传给前端 return render(request, 'reg.html', {'form_obj': form_obj})
1、forms组件渲染页面的第一种方式
自动生成多个p标签,本地测试方便,封装程度太高了,不便于扩展
前端reg.html代码:
<h2>第一种渲染方式:自动生成多个p标签,本地测试方便,封装程度太高了,不便于扩展</h2> {{ form_obj.as_p }}
2、forms组件渲染页面的第二种方式
扩展性较高
前端reg.html代码:
<h2>第二种渲染方式:扩展性较高</h2> <label for="{{ form_obj.username.id_for_label }}"> {{ form_obj.username.label }}{{ form_obj.username }} </label> <label for="{{ form_obj.password.id_for_label }}"> {{ form_obj.password.label }}{{ form_obj.password }} </label> <label for="{{ form_obj.email.id_for_label }}"> {{ form_obj.email.label }}{{ form_obj.email }} </label>
3、forms组件渲染页面的第三种方式
for循环(推荐使用)
前端reg.html代码:
<h2>第三种方式:for循环 推荐使用</h2> <form action="" method="post" novalidate> {% for foo in form_obj %} <p> {{ foo.label }}:{{ foo }} <span style="color: red">{{ foo.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form>
四、如何使用forms组件渲染错误信息
数据校验一个前后端都得有,但是前端的校验安全性很差,可有可无,而后端的校验则必须非常全面
1、取消浏览器自动校验的功能
<form action="" method="post" novalidate></form> // novalidate属性,使前端不对数据进行处理,直接传给后端。然后后端通过逻辑代码进行校验
2、演示:后端校验结果在前端渲染
后端views.py代码:
def reg(request): # 1 先生成一个空对象 form_obj = MyRegForm() if request.method == 'POST': # 3 获取用户属性并交给forms组件进行校验 form_obj = MyRegForm(request.POST) # 4 获取校验结果 if form_obj.is_valid(): return HttpResponse('数据正确') else: # 获取校验失败的字段和提示信息在后端显示 print(form_obj.errors) # 2 直接将该对象传给前端 return render(request, 'reg.html', {'form_obj': form_obj})
前端reg.html代码:
<h2>第三种方式:for循环 推荐使用</h2> <form action="" method="post" novalidate> {% for foo in form_obj %} <p> {{ foo.label }}:{{ foo }} // 模板语法索引取错误信息,当向后端提交数据时,后端通过forms组件校验生成错误信息,前端再拿到错误信息,展示在前端界面,模板语法索引溢出不会报错 <span style="color: red">{{ foo.errors.0 }}</span></p> {% endfor %} <input type="submit"> </form>
五、forms组件常用参数
1、label
input的提示信息
label='用户名', # 设置输入框提示
2、err_messages
自定义报错的提示信息
error_messages={ # 设置输入不符合规范的字符之后的报错信息 'max_length': '用户名最长8位', 'min_length': '用户名最短3位', 'required': '用户名不能为空', }
3、required
设置字段是否允许为空
required=False, # 设置False,可以提交空;默认为True,不能提交空
4、initial
设置默认值
initial='tom', # 添加输入框内的默认值
5、widget
控制type类型以及属性
from django.forms import widgets # 先导入方法 widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), # 输入框为明文文本,括号内是为输入框添加属性、样式等 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) # 输入框为密文,括号内是为输入框添加属性、样式等
6、例子演示常用参数用法
后端views.py代码:
from django.shortcuts import render, HttpResponse, redirect from django import forms from django.forms import widgets from django.core.validators import RegexValidator class MyRegForm(forms.Form): username = forms.CharField(max_length=8, min_length=3, label='账户', # 设置输入框提示 # required=False, # 默认为True,不能提交空,设置False,可以提交空 initial='tom', # 添加输入框内的默认值 # 输入框为明文文本,attrs属性括号内是为输入框添加属性、样式等 widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), error_messages={ # 设置输入不符合规范的字符之后的报错信息 'max_length': '用户名最长8位', 'min_length': '用户名最短3位', 'required': '用户名不能为空', }) password = forms.CharField(max_length=8, min_length=3, label='密码', # 输入框为密文,attrs属性括号内是为输入框添加属性、样式等 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}), error_messages={ # 设置输入不符合规范的字符之后的报错信息 'max_length': '密码最长8位', 'min_length': '密码最短3位', 'required': '密码不能为空', }) # email字段必须填写符合邮箱格式的数据 email = forms.EmailField(label='邮箱', initial='xxx@qq.com', # 添加输入框内的默认值 widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), error_messages={ # 设置输入不符合规范的字符之后的报错信息 'required': '邮箱必须填写', 'invalid': '邮箱格式不正确' }) # 正则校验,校验手机号 phone = forms.CharField(label='手机号码', validators=[ RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头') ], initial='159xxxxxxxx', # 添加输入框内的默认值 widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), error_messages={ # 设置输入不符合规范的字符之后的报错信息 'required': '电话号码必须填写', 'invalid': '电话号码格式不正确' }) def reg(request): # 1.先生成一个空对象 form_obj = MyRegForm() if request.method == 'POST': # 3.获取用户属性并交给forms组件进行校验 form_obj = MyRegForm(request.POST) # 4.获取校验结果 if form_obj.is_valid(): return HttpResponse('数据正确') # 2.直接将该对象传给前端 return render(request, 'reg.html', {'form_obj': form_obj})
前端reg.html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css"> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> <h2>注册页面</h2> {% for foo in form_obj %} <p> {{ foo.label }}:{{ foo }} <span style="color: red">{{ foo.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form> </div> </div> </div> </body> </html>
七、钩子函数
钩子函数,就是再加一层校验方式。看一个例子,体验全局钩子、局部钩子的作用:
后端views.py代码:
from django.shortcuts import render, HttpResponse, redirect from django import forms from django.forms import widgets class DoublePassword(forms.Form): username = forms.CharField(max_length=8, min_length=3, label='账户', # 设置输入框提示 # required=False, # 默认为True,不能提交空,设置False,可以提交空 initial='tom', # 添加输入框内的默认值 # 输入框为明文文本,attrs属性括号内是为输入框添加属性、样式等 widget=forms.widgets.TextInput(attrs={'class': 'form-control'}), error_messages={ # 设置输入不符合规范的字符之后的报错信息 'max_length': '用户名最长8位', 'min_length': '用户名最短3位', 'required': '用户名不能为空', }) password = forms.CharField(max_length=8, min_length=3, label='密码', # 输入框为密文,attrs属性括号内是为输入框添加属性、样式等 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}), error_messages={ # 设置输入不符合规范的字符之后的报错信息 'max_length': '密码最长8位', 'min_length': '密码最短3位', 'required': '密码不能为空', }) confirm_password = forms.CharField(max_length=8, min_length=3, label='确认密码', # 输入框为密文,attrs属性括号内是为输入框添加属性、样式等 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}), error_messages={ # 设置输入不符合规范的字符之后的报错信息 'max_length': '确认密码最长8位', 'min_length': '确认密码最短3位', 'required': '确认密码不能为空', }) # 全局钩子 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', '两次密码不一致') return self.cleaned_data # 局部钩子 def clean_username(self): username = self.cleaned_data.get('username') if 'aaa' in username: self.add_error('username', '账户中不能含有aaa') return username def double_password(request): # 1.先生成一个空对象 password_obj = DoublePassword() if request.method == 'POST': # 3.获取用户属性并交给forms组件进行校验 password_obj = DoublePassword(request.POST) # 4.获取校验结果 if password_obj.is_valid(): return HttpResponse('数据正确') # 2.直接将该对象传给前端 return render(request, 'double_password.html', {'password_obj': password_obj})
前端double_password.html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css"> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> <h2>测试密码</h2> {% for password in password_obj %} <p> {{ password.label }}:{{ password }} <span style="color: red">{{ password.errors.0 }}</span> </p> {% endfor %} <input type="submit"> </form> </div> </div> </div> </body> </html>
1、全局钩子(针对多个字段)
验证:校验密码和确认密码是否一致
2、局部函数(针对单个字段)
验证:校验密码中不能含有aaa
PS:如果想要同时操作多个字段的数据,那么就用全局钩子
如果想要操作单个字段的数据,那么就用局部钩子