django forms的常用命令及方法(三)
由于本人整理需要,抄至:https://www.cnblogs.com/haowen980/p/9277257.html (源文)
校验字段功能
之前写的视图函数,提交的数据,没有做校验,就调教到数据库里面了。这样是不对的。
比如:用户名,必须要符合一定的长度。密码复杂度,等等。
forms组件最大的作用,就是做数据校验。
普通做法,一个一个写校验规则,没有解耦。校验规则,都在视图函数里面。
新建项目formDemo
新建urls.py ,新增路径index
1 from app01 import views
2 urlpatterns = [
3 path('admin/', admin.site.urls),
4 path('index/', views.index),
5 ]
修改views.py ,新增index视图函数,form组件先放到视图函数
单独起一个类,后续会分离出来
1 from django.shortcuts import render 2 3 # Create your views here. 4 from django import forms # 必须导入模块 5 class DemoForm(forms.Form): # 必须继承Form 6 #限制数据为字符串,最大长度32 7 name = forms.CharField(max_length=32) 8 age = forms.IntegerField() # 限制为数字 9 email = forms.EmailField() # 限制为邮箱格式 10 11 def index(request): 12 return render(request,"index.html")
templates新增index.html,里面是空的
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 9 </body> 10 </html>
打开Pycharm,点击左下角的Django Console

输入以下命令,导入视图函数的DemoForm类
1 from app01.views import DemoForm
效果如下:

DemoForm类是用来做校验的,它接收一个字典。字典必须包含2个key,分别是name,age,email
测试一个字典数据
执行下面2个命令
1 form=DemoForm({"name":"john","age":"21","email":"123@163.com"}) 2 form.is_valid()
效果如下:输出True

解释:
is_valid()表示执行校验,如果3个key都符合要求,输出True
测试1:age不符合

3个必须同时成立才行

在DemoForm里面,等式左边对应的是key,等式右边对应校验规则
它有一个默认规则,不能为空
测试2:少一个字段

从结果上来看,也是可以通过的。
它只校验指定的字段,那么额外的键值,会忽略
网页校验
修改urls.py,增加路径addbook
1 from app01 import views 2 urlpatterns = [ 3 path('admin/', admin.site.urls), 4 path('index/', views.index), 5 path('addbook/', views.addbook), 6 ]
修改views.py,增加addbook视图函数,完整代码如下:
1 from django.shortcuts import render,HttpResponse 2 3 # Create your views here. 4 from django import forms 5 class DemoForm(forms.Form): 6 name = forms.CharField(max_length=32) 7 age = forms.IntegerField() 8 email = forms.EmailField() 9 class UserForm(forms.Form): 10 #限制数据为字符串,最小长度4,最大长度12 11 name = forms.CharField(min_length=4,max_length=12) 12 age = forms.IntegerField() #限制为数字 13 #限制长度为11位 14 tel = forms.CharField(min_length=11,max_length=11) 15 16 17 def index(request): 18 19 return render(request,"index.html") 20 21 def addbook(request): 22 if request.method == "POST": 23 #将post数据传给UserForm 24 form = UserForm(request.POST) 25 if forms.is_valid(): #验证数据 26 print("success") 27 else: 28 print("fail") 29 return HttpResponse("ok") 30 31 return render(request,"addbook.html")
templates新增addbook.html
做表单校验的时候,一定要注意,表单的name和class的属性必须一一对应
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h3>添加用户</h3> 9 <form action="" method="post"> 10 {% csrf_token %} 11 <lable>姓名</lable><input type="text" name="name"/><br/> 12 <lable>年龄</lable><input type="text" name="age"/><br/> 13 <lable>邮箱</lable><input type="text" name="email"/><br/> 14 <lable>手机号码</lable><input type="text" name="tel"/> 15 <br/> 16 <input type="submit"> 17 </form> 18 </body> 19 </html>
网页访问添加页面,输出信息

提交之后

控制台输出:

空表单直接提交

Pycharm控制台输出:fail
is_valid()
form.is_valid() 它做了2件事情:
1.将数据传给form
2.将验证数据拆分到2个容器中
self.cleandtate={}表示干净的数据
self.error = {}表示验证不通过的数据
self表示UserForm类的实例对象
addbook视图函数
def addbook(request):
if request.method == "POST":
print(request.POST)
# 将post数据传给UserForm
form = UserForm(request.POST)
if form.is_valid(): # 验证数据
print("###success###")
print(form.cleaned_data)
print(form.errors)
else:
print("###fail###")
print(form.cleaned_data)
print(form.errors)
print(type(form.errors))
return HttpResponse("ok")
return render(request,"addbook.html")
再次提交数据

Pycharm控制台输出:

虽然POST发送了5个键值,但是UserForm只校验3个键值。
form.cleaned_data 输出了3个键值
form.errors 输出的内容空,它的类型为ErrorDict
只要有一个错误,就会走else
错误数据演示
修改views.py,修改UserForm,增加邮箱
class UserForm(forms.Form):
#限制数据为字符串,最小长度4,最大长度12
name = forms.CharField(min_length=4,max_length=12)
age = forms.IntegerField() #限制为数字
email = forms.EmailField() #限制为邮箱格式
#限制长度为11位
tel = forms.CharField(min_length=11,max_length=11)
输入一个错误的表单

Pycharm控制台输出:

form.errors输出了一段Html标签,提示邮箱格式错误
提取email错误信息
修改UserForm
1 def addbook(request): 2 if request.method == "POST": 3 # 将post数据传给UserForm 4 form = UserForm(request.POST) 5 print(request.POST) 6 if form.is_valid(): # 验证数据 7 print("###success###") 8 print(form.cleaned_data) # 所有干净的字段以及对应的值 9 # ErrorDict : {"校验错误的字段":["错误信息",]} 10 print(form.errors) 11 print(type(form.errors)) # 打印 12 else: 13 print("###fail###") 14 print(form.cleaned_data) 15 print(form.errors) 16 # 获取email错误信息,返回一个错误列表,可以切片 17 print(form.errors.get("email")) 18 # 获取第一个错误信息 19 print(form.errors.get("email")[0]) 20 21 return HttpResponse("ok") 22 23 return render(request,"addbook.html")
pycharm控制台输出

form.errors.get("email") 可以提取email的错误信息,它返回的是一个错误列表
通过切片,可以获取第一个错误信息
渲染标签功能
渲染方式1
使用自带的模板属性渲染
上面讲的form表单里面的元素,是手动写的。form组件可以帮你实现渲染表单元素!
那么需要渲染哪些元素,取决于UserForm这个自定义类的属性来决定的
举例:
修改addbook视图函数
1 def addbook(request): 2 if request.method == "POST": 3 # 将post数据传给UserForm 4 form = UserForm(request.POST) 5 print(request.POST) 6 if form.is_valid(): # 验证数据 7 print("###success###") 8 print(form.cleaned_data) # 所有干净的字段以及对应的值 9 # ErrorDict : {"校验错误的字段":["错误信息",]} 10 print(form.errors) 11 print(type(form.errors)) # 打印 12 else: 13 print("###fail###") 14 print(form.cleaned_data) 15 print(form.errors) 16 # 获取email错误信息,返回一个错误列表,可以切片 17 print(form.errors.get("email")) 18 # 获取第一个错误信息 19 print(form.errors.get("email")[0]) 20 21 return render(request, "adduser.html", {"form":form}) 22 23 else: 24 form = UserForm() 25 return render(request,"addbook.html",{"form":form})
修改addbook.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h3>添加用户</h3> 9 <form action="" method="post"> 10 {% csrf_token %} 11 {{ form.as_p }} 12 <br/> 13 <input type="submit"> 14 </form> 15 </body> 16 </html>
as_p是一个特殊的属性,常见的有:
{{ form.as_table }}以表格的形式将它们渲染在<tr>标签中{{ form.as_p }}将它们渲染在<p>标签中{{ form.as_ul }}将它们渲染在<li>标签中
访问页面,效果如下:

使用浏览器工具,查看html代码
它使用了P标签来包裹

laber的for属性和input的id属性是对应的。id的名字和UserForm类定义的属性是类似的,加了id_前缀。
lable的显示的文字和UserForm类定义的属性是一样的,首字母大写了!
input的name属性和UserForm类定义的属性是一样的
默认自带required属性,不允许内容为空。
minlength的属性来源于UserForm类的定义。
注意:form组件只能渲染表单里面的元素,比如input标签。除此之外,其他的需要手写!
它的样式,太丑了!
渲染方式2
使用自定义的标签来包裹form变量
举例:
更改addbook.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h3>添加用户</h3> 9 <form action="" method="post"> 10 {% csrf_token %} 11 <div> 12 <p>姓名</p> 13 {{ form.name }} 14 </div> 15 <div> 16 <p>年龄</p> 17 {{ form.age }} 18 </div> 19 <div> 20 <p>邮箱</p> 21 {{ form.email }} 22 </div> 23 <div> 24 <p>手机号码</p> 25 {{ form.tel }} 26 </div> 27 <input type="submit"> 28 </form> 29 </body> 30 </html>
刷新页面效果如下:

渲染方式3
使用for循环渲染
修改addbook.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h3>添加用户</h3> 9 <form action="" method="post"> 10 {% csrf_token %} 11 {% for field in form %} 12 <div> 13 <label for=""> {{ field.label }}</label> 14 {{ field }} 15 </div> 16 {% endfor %} 17 <input type="submit"> 18 </form> 19 </body> 20 </html>
刷新网页,效果如下:

field.label 表示属性名
field 表示input输入框
显示中文
将label换成中文,需要增加label属性
修改views.py里面的UserForm类
1 class UserForm(forms.Form): # 必须继承Form 2 #限制数据为字符串,最小长度4,最大长度12 3 name = forms.CharField(min_length=4,max_length=12,label="姓名") 4 age = forms.IntegerField(label="年龄") # 限制为数字 5 email = forms.EmailField(label="邮箱") # 限制为邮箱格式 6 #限制长度为11位 7 tel = forms.CharField(min_length=11,max_length=11,label="手机号码")
刷新网页,效果如下:

美化input输入框
需要使用bootstrap
修改urls.py,修改路径、
1 urlpatterns = [ 2 path('admin/', admin.site.urls), 3 path('index/', views.index), 4 path('adduser/', views.adduser), 5 ]
修改views.py,将addbook重名为adduser
1 def adduser(request): 2 if request.method == "POST": 3 # 将post数据传给UserForm 4 form = UserForm(request.POST) 5 print(request.POST) 6 if form.is_valid(): # 验证数据 7 print("###success###") 8 print(form.cleaned_data) # 所有干净的字段以及对应的值 9 # ErrorDict : {"校验错误的字段":["错误信息",]} 10 print(form.errors) 11 print(type(form.errors)) # 打印 12 else: 13 print("###fail###") 14 print(form.cleaned_data) 15 print(form.errors) 16 # 获取email错误信息,返回一个错误列表,可以切片 17 print(form.errors.get("email")) 18 # 获取第一个错误信息 19 print(form.errors.get("email")[0]) 20 21 return render(request, "adduser.html", {"form":form}) 22 23 else: 24 form = UserForm() 25 return render(request,"adduser.html",{"form":form})
将addbook.html,重命名为adduser.html
引入bootstrap,代码如下:
访问url: http://127.0.0.1:8000/adduser/
效果如下:

这里面input用的还是默认样式,只要给input标签增加class="form-group",就有美化效果!
由于input是form组件渲染的,不能直接添加class,需要在UserForm类里面,指定class
修改UserForm类之前,导入一个模块widgets
Widgets
widget 是Django 对HTML输入元素的表示。Widget 负责渲染HTML和提取GET/POST 字典中的数据。
如果你想让某个Widget 实例与其它Widget 看上去不一样,你需要在Widget 对象实例化并赋值给一个表单字段时指定额外的属性(以及可能需要在你的CSS 文件中添加一些规则)
修改views.py,完整代码如下:
1 from django.shortcuts import render,HttpResponse 2 3 # Create your views here. 4 from django import forms 5 from django.forms import widgets 6 # class DemoForm(forms.Form): 7 # name = forms.CharField(max_length=32) 8 # age = forms.IntegerField() 9 # email = forms.EmailField() 10 class UserForm(forms.Form): 11 #定义变量,专门给text类型的输入框加class 12 wid = widgets.TextInput(attrs={"class":"form-control"}) 13 #限制数据为字符串,最小长度4,最大长度12 14 name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid) 15 age = forms.IntegerField(label="年龄",widget=wid) #限制为数字 16 email = forms.EmailField(label="邮箱",widget=wid) #限制为邮箱格式 17 #限制长度为11位 18 tel = forms.CharField(min_length=11,max_length=11,label="手机号",widget=wid) 19 20 def index(request): 21 22 return render(request,"index.html") 23 24 def adduser(request): 25 if request.method == "POST": 26 print(request.POST) 27 # 将post数据传给UserForm 28 form = UserForm(request.POST) 29 if form.is_valid(): # 验证数据 30 print("###success###") 31 print(form.cleaned_data) 32 print(form.errors) 33 else: 34 print("###fail###") 35 print(form.cleaned_data) 36 print(form.errors) 37 print(type(form.errors)) 38 # 获取email错误信息,返回一个错误列表,可以切片 39 print(form.errors.get("email")) 40 # 获取第一个错误信息 41 print(form.errors.get("email")[0]) 42 43 return render(request, "adduser.html",{"form":form}) 44 45 else: 46 form = UserForm() 47 return render(request, "adduser.html", {'form':form}) 48 49 views.py
直接提交空数据,页面有错误提示
注意:这个提示是bootstrap做的,不是form组件

虽然jquery可以直接对表单进行验证,判断为空,或者其他规则。但是客户端浏览器的js代码,是可以跳过验证的。直接提交数据给服务器,如果服务器没有做数据校验,那么将面临风险!
修改adduser.html,在form标签后面增加novalidate,表示关闭bootstrap表单验证
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> 7 </head> 8 <body> 9 10 <div class="container"> 11 <div class="row"> 12 <div class="col-md-6 col-md-offset-2"> 13 <h3>添加用户</h3><br/> 14 <form action="" method="post" novalidate> 15 {% csrf_token %} 16 {% for field in form %} 17 <div class="form-group"> 18 <label for="">{{ field.label }}</label> 19 {{ field }} 20 </div> 21 {% endfor %} 22 <br/> 23 <input type="submit" class="btn btn-success btn-sm"> 24 </form> 25 </div> 26 </div> 27 </div> 28 29 </body> 30 </html>
刷新页面,填3个值,最后一个故意不填写

发现刚刚增加的数据,没有了。这样用户体验不好!用户得小心翼翼的输入每一个数据!
查看Pycharm控制台输出:
###fail### <ul class="errorlist"><li>tel<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
发现它走了else的代码,使用render时,没有传值。导致页面为空!
修改views.py
给adduser.html传一个form。注意:此时的form变量是带有表单数据的!
1 def adduser(request): 2 if request.method == "POST": 3 # 将post数据传给UserForm 4 form = UserForm(request.POST) 5 print(request.POST) 6 if form.is_valid(): # 验证数据 7 print("###success###") 8 print(form.cleaned_data) # 所有干净的字段以及对应的值 9 # ErrorDict : {"校验错误的字段":["错误信息",]} 10 print(form.errors) 11 print(type(form.errors)) # 打印 12 return HttpResponse("添加成功") 13 else: 14 print("###fail###") 15 # print(form.cleaned_data) 16 print(form.errors) 17 # # 获取email错误信息,返回一个错误列表,可以切片 18 # print(form.errors.get("email")) 19 # # 获取第一个错误信息 20 # print(form.errors.get("email")[0]) 21 return render(request, "adduser.html", {"form":form}) 22 23 else: # 默认是get请求(地址栏输入访问时) 24 form = UserForm() # 没有表单数据的form 25 return render(request,"adduser.html",{"form":form})
再次刷新页面,数据就回来了!

数据怎么就回来了呢?
因为既然是POST请求,而且携带了POST数据,必然执行了form.is_valid()
虽然没有验证通过,但是执行下面一句代码时
return render(request, "adduser.html", {"form":form})
此时的form是含有POST表单数据的,所以页面才会渲染出来!
注意:当input属性为password时,是不会渲染的!除此之外,其他的表单元素,是可以渲染的!
提交一个正确的数据

提示添加成功

显示错误信息
约定俗成,使用span标签来显示错误信息
修改adduser.html,增加span标签
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> 7 </head> 8 <body> 9 10 <div class="container"> 11 <div class="row"> 12 <div class="col-md-6 col-md-offset-2"> 13 <h3>添加用户</h3> 14 <form action="" method="post" novalidate> 15 {% csrf_token %} 16 {% for field in form %} 17 <div> 18 <label for=""> {{ field.label }}</label> 19 {{ field }}<label>{{ field.errors.0 }}</label> 20 </div> 21 {% endfor %} 22 <input type="submit" class="btn btn-success btn-sm"> 23 </form> 24 </div> 25 </div> 26 </div> 27 </body> 28 </html>
解释:
field.errors 表示错误列表。因为是列表,会有很多错误信息
field.errors.0 表示接收第一个错误信息。一般取第一个即可!
那么问题来了,get请求时,比如地址栏访问页面,他是娶不到值得,那么span标签是空得,但是不影响页面展示
直接提交空数据,页面会有英文提示,它表示此字段不允许为空

显示黑色,不好看,加一个样式
修改adduser.html ,增加样式,pull-right表示右对齐
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> 7 </head> 8 <body> 9 10 <div class="container"> 11 <div class="row"> 12 <div class="col-md-6 col-md-offset-2"> 13 <h3>添加用户</h3> 14 <form action="" method="post" novalidate> 15 {% csrf_token %} 16 {% for field in form %} 17 <div class="form-group"> 18 <label for=""> {{ field.label }}</label> 19 {{ field }}<span class="error pull-right">{{ field.errors.0 }}</span> 20 </div> 21 {% endfor %} 22 <input type="submit" class="btn btn-success btn-sm"> 23 </form> 24 </div> 25 </div> 26 </div> 27 </body> 28 </html>
刷新页面,效果如下:

错误信息显示中文
显示中文需要在UserForm类中的字段增加error_message属性
1 class UserForm(forms.Form): #必须继承Form 2 #定义变量,专门text类型的输入框添加class 3 wid = widgets.TextInput(attrs={"class":"form-control"}) 4 #定义字典,错误信息显示中文 5 #限制数据为字符串,最小长度4,最大长度12 6 error_hints = {"required":"该字段不能为空"} 7 name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid,error_messages=error_hints) 8 #限制为数字 9 age = forms.IntegerField(label="年龄",widget=wid,error_messages=error_hints) 10 #限制为邮箱格式 11 email = forms.EmailField(label="邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}),error_messages=error_hints) 12 #限制长度为11位 13 tel = forms.CharField(min_length=11,max_length=11,label="手机号码",widget=wid,error_messages=error_hints)
解释:
error_messages 用来定义错误信息,可以定义多个错误类型!它接收一个字典
required 表示为空时,输出This field is required.
那么要定义中文时,重新赋值即可。需要显示日文,韩文,法文...的,自己定义吧!
重新访问页面,输入第一个值,提交。其它字段会有错误提示!

当输入的字符串与输入框要求不匹配时,却提示的是一段英文信息,不行,得改。

修改UserForm类,修改这一行
error_hints = {"required":"该字段不能为空","invalid":"格式错误!"}

核心问题,必须要明白,错误信息为什么会显示出来?
执行is_valid(),就会执行校验动作。如果不通过,那么form变量就会包含错误信息。
通过form组件渲染错误信息,页面就展示出来
局部钩子与全局钩子
上面提到的校验规则是forms组件自带的。 它做了一些简单的校验功能,比如判断字符串,纯数字,邮箱等等。
比如要求用户名,必须包含字母和数字。年龄必须要大于18岁,手机号码要以136,137开头...
这些需求,默认的校验规则是做不到的。
我们想要自行设计校验的规则,Django给我们提供了钩子。
先来看一段源码:
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
这段源码能够设置钩子的来源。
局部钩子
导入模块
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
举例:
要求用户名不能是纯数字
1 def clean_name(self): 2 val = self.cleaned_data.get("name") #获取输入的用户名 3 4 if not val.isdigit(): #判断不是数字类型 5 6 return val 7 else: 8 raise ValidationError("用户名不能是纯数字")
注意:
clean_name 这个名字是有含义的,不能随便定义。name表示UserForm类的属性。clean表示校验 val 表示用户输入的用户名
val.isdigit() 表示判断输入的是否为数字,必须return一个值
raise 表示主动报错,必须接ValidationError。
上面这些要求是源代码定义的,具体可以看源代码。
views.py,完整代码如下:
1 class UserForm(forms.Form): 2 #定义变量,专门给text类型的输入框添加class 3 wid = widgets.TextInput(attrs={"class":"form-control"}) 4 #定义字典,错误信息显示中文 5 #限制数据为字符串,最小长度4,最大长度12 6 error_hints = {"required":"该字段不能为空","invalid":"格式错误!"} 7 name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid,error_messages=error_hints) 8 #限制数字 9 age = forms.IntegerField(label="年龄",widget=wid,error_messages=error_hints) 10 #限制为邮箱格式 11 email = forms.EmailField(label="邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}),error_messages=error_hints) 12 #限制长度为11位 13 tel = forms.CharField(min_length=11,max_length=11,label="手机号码",widget=wid,error_messages=error_hints) 14 15 def clean_name(self): 16 val = self.cleaned_data.get("name") # 获取输入的用户名 17 18 if not val.isdigit(): # 判断不是数字类型 19 return val 20 else: 21 raise ValidationError("用户名不能是纯数字")
手机号码必须11位
修改UserForm类,增加clean_tel方法,完整代码如下:

手机号码必须11位
修改UserForm类,增加clean_tel方法,完整代码如下:
1 class UserForm(forms.Form): 2 #定义变量,专门给text类型的输入框添加class 3 wid = widgets.TextInput(attrs={"class":"form-control"}) 4 #定义字典,错误信息显示中文 5 #限制数据为字符串,最小长度4,最大长度12 6 error_hints = {"required":"该字段不能为空","invalid":"格式错误!"} 7 name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid,error_messages=error_hints) 8 #限制数字 9 age = forms.IntegerField(label="年龄",widget=wid,error_messages=error_hints) 10 #限制为邮箱格式 11 email = forms.EmailField(label="邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}),error_messages=error_hints) 12 #限制长度为11位 13 tel = forms.CharField(max_length=11,label="手机号码",widget=wid,error_messages=error_hints) 14 15 def clean_name(self): 16 val = self.cleaned_data.get("name") # 获取输入的用户名 17 18 if not val.isdigit(): # 判断不是数字类型 19 return val 20 else: 21 raise ValidationError("用户名不能是纯数字") 22 23 def clean_tel(self): 24 val = self.cleaned_data.get("tel") 25 if len(val) == 11: # 判断长度 26 return val 27 else: 28 raise ValidationError("手机号码必须11位")
注意:要去除tel里面的min_length,这是不严谨的写法!
重新访问页面,测试一下

年龄必须18岁以上
增加clean_age方法
1 def clean_age(self): 2 val = self.cleaned_data.get("age") 3 if int(val) > 18: # input输入的值为字符串,必须转换为int 4 return val 5 else: 6 raise ValidationError("年龄必须满18岁以上!")
重新访问页面,测试一下

注意:
is_valid执行时,才会执行校验
这里有2层校验。第一层校验是UserForm定义的那些属性,比如判断字符串或者数字的。第二层校验是clean属性名定义的这些方法。只有通过第一层校验时,才会进入第二层校验。
不论是第一层还是第二层,通过校验后,将key_value放到cleaned_data容器里面,不通过校验时,将key_value 放到errors容器里面
查看源代码
先找到views.py里面的is_valid,使用ctrl+鼠标左键,点击is_valid。他会调到is_valid方法的源代码,点击self.errors-->full_clean()-->self._clean_fields()
_clean_fields原代码如下:
1 def _clean_fields(self): 2 for name, field in self.fields.items(): 3 # value_from_datadict() gets the data from the data dictionaries. 4 # Each widget type knows how to retrieve its own data, because some 5 # widgets split data over several HTML fields. 6 if field.disabled: 7 value = self.get_initial_for_field(field, name) 8 else: 9 value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) 10 try: 11 if isinstance(field, FileField): 12 initial = self.get_initial_for_field(field, name) 13 value = field.clean(value, initial) 14 else: 15 value = field.clean(value) 16 self.cleaned_data[name] = value 17 if hasattr(self, 'clean_%s' % name): 18 value = getattr(self, 'clean_%s' % name)() 19 self.cleaned_data[name] = value 20 except ValidationError as e: 21 self.add_error(name, e)
它会对表单的每一个数据,使用for循环处理。field指的是UserForm定义的那些属性
self.fields.items() 这里的self.fields数据,可能是这样的。
self.fields={"name":name的field对象,"age":age的field对象...}
self.fields.items() ,就能拿到一个field对象。这个field是是一个规则对象.
下面是一个完整例子:
1 在views.py中 2 3 from django.shortcuts import render,HttpResponse,redirect 4 from django.views import View 5 from .models import Fruit,Buyer 6 from django import forms 7 class buyerform(forms.Form): 8 error_hints= {"required":"该字段不能为空","invalid":"格式错误!"} 9 name=forms.CharField(max_length=12,label="名字",error_messages=error_hints) 10 age=forms.IntegerField(label="年龄",error_messages=error_hints) 11 email=forms.EmailField(max_length=32,label="邮箱",error_messages={"required":"该字段不能为空","invalid":"格式错误!"}) 12 tel=forms.CharField(min_length=11,max_length=11,label="电话",error_messages=error_hints) 13 password=forms.CharField(label="密码",error_messages=error_hints,widget=forms.PasswordInput) 14 def clean_password(self): 15 val=self.cleaned_data.get('password') 16 if len(val)>5: 17 return val 18 else: 19 raise forms.ValidationError("密码必须5位或5位以上") 20 21 22 class index(View): 23 def get(self,request): 24 form = buyerform() 25 return render(request,'adduser.html',{'form':form}) 26 27 def post(self,request): 28 form=buyerform(request.POST) 29 if form.is_valid(): 30 print "sucess" 31 print form.cleaned_data 32 print form.errors 33 return HttpResponse('OK') 34 else: 35 print "fail" 36 print form.cleaned_data 37 print form.errors 38 print form.errors.get('age') 39 print type(form.errors.get('age')) 40 return render(request,'adduser.html',{'form':form})
1 在html中 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 {# <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">#} 8 </head> 9 <body> 10 <div class="container"> 11 <div class="row"> 12 <div class="col-md-6 col-md-offset-2"> 13 <h3>添加用户</h3><br/> 14 <form action="" method="post" novalidate> 15 {% csrf_token %} 16 {% for field in form %} 17 <div class="form-group"> 18 <label for="">{{ field.label }}</label> 19 {{ field }}<span class="error pull-right">{{ field.errors.0 }}</span> 20 </div> 21 {% endfor %} 22 <br/> 23 <input type="submit" class="btn btn-success btn-sm"> 24 </form> 25 </div> 26 </div> 27 </div> 28 </body> 29 </html>


浙公网安备 33010602011771号