我在之前做了一個關於AJAX和form組件的筆記,可以參考:Django學習筆記(8)——前后台數據交互實戰(AJAX);Django學習筆記(6)——Form表單
我覺得自己在寫Django筆記(8)的時候,我只是對AJAX有個粗略的了解,就明白其兩大優點,局部刷新和異步交互。Form組件知識雖然大多數都明白了,但是對局部鈎子和全局鈎子還不是很清楚。留了個坑,所以在以后的學習中,肯定會再遇到。
現在,我感覺自己將關於AJAX和Django的數據交互這部分了解清楚了,而且將Form組件的鈎子也理解透徹了。特意做個筆記記錄自己的學習過程。也是在Django筆記(8)的時候說過的,我將這段內容理清楚了,會寫一個易於理解的文章鞏固,而這篇文件就是。
下面我分別將自己所做的AJAX知識筆記,Form組件中局部鈎子和全局鈎子的筆記展示一下。其中局部鈎子和全局鈎子都做了源碼分析,是為了更好的理解其原理吧。
AJAX知識
1,AJAX請求頭中常見contentType
數據發送出去,還需要服務端解析成功才有意義。Python內置了自動解析常見數據格式的功能。服務端通常是根據請求頭(headers)中的Content-Type 字段獲取請求頭中的消息主體是用何種方式編碼,再對主體進行解析。所以說到POST提交數據方案,包含了Content-Type 和消息主體編碼方式兩部分。
下面我們一起來看看ajax中POST請求的Content-Type。以application開頭的媒體格式類型:
application/xhtml+xml :XHTML格式 application/xml : XML數據格式 application/atom+xml :Atom XML聚合格式 application/json : JSON數據格式 application/pdf :pdf格式 application/msword : Word文檔格式 application/octet-stream : 二進制流數據(如常見的文件下載) application/x-www-form-urlencoded : <form encType=””>中默認的encType,form表單數據被編碼為key/value格式 發送到服務器(表單默認的提交數據的格式)
ContentType指的是請求體的編碼類型,常見的有三種:
1.1,application/x-www-form-urlencoded
這應該是最常見的POST提交數據的方式了。瀏覽器的原生表單<form>默認的提交方式,如果不設置enctype屬性,那么最終就會以application/x-www-form-urlencoded 方式提交數據。請求類似於下面這樣(無關的請求頭在本文中都省略掉了):
POST http://www.example.com HTTP/1.1 Content-Type: application/x-www-form-urlencoded;charset=utf-8 user=james&age=24
下面將參數的默認使用方法總結如下:
data: 當前ajax請求要攜帶的數據,是一個object對象,ajax方法就會默認地把 它編碼成某種格式(urlencoded:?a=1&b=2)發送給服務端;此外,ajax默認以get方 式發送請求。 contentType:"application/x-www-form-urlencoded"。發送信息至服務器時內容 編碼類型。用來指明當前請求的數據編碼格式;urlencoded:?a=1&b=2;

1.2,multipart/form-data
這又是一個常見的POST數據提交的方式。我們使用表單上傳文件時,必須讓表單的enctype 等於 multipart/form-data。
直接看一個例子:
POST http://www.example.com HTTP/1.1 Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA ------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="user" james ------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="file"; filename="chrome.png" Content-Type: image/png PNG ... content of chrome.png ... ------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
這個例子稍微復雜點,首先生成了一個boundary 用於分割不同的字段,為了避免與正文內容重復,boundary很長很復雜。然后Content-Type里指明了數據是以 multipart/form-data 來編碼。
本次請求的 boundary 是什么內容?
消息主體里是按照字段個數又分為多個結構類似的部分,每部分都是以 --boundary 開始。緊接着是內容描述信息,然后是回車,最后是字段具體內容(文本或者二進制)。如果傳輸的是文件,還要包含文本名和文件類型信息。消息主體最后以 --boundary-- 標識結束。關於 multipart/form-data 的詳細定義,請前往 rfc 1967 查看。
這種方式一般用來上傳文件,各大服務端語言對它也有着良好的支持。
上面提到的這兩種POST數據的方式,都是瀏覽器原生支持的,而且現階段標准中原生表單也只支持這兩種方式。其實enctype 還支持 text/plain,不過用的非常少。
隨着越來越多的Web站點,尤其是WebAPP,全部使用AJAX進行數據交互之后,我們完全可以定義新的數據提交方式,給開發帶來更多便利。
1.3,application/json
application/json 這個Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人吧他作為請求頭,用來告訴服務端消息主體是序列化的JSON字符串。由於JSON規范的流行,處理低版本IE之外的各大瀏覽器都支持原生的JSON.stringify,服務端語言也有處理JSON的函數,使用JSON不會遇到什么麻煩。
上述這種默認參數形式,data中的csrf跨站請求偽造鍵值對會被中間件自動識別,ContentType參數還有如下一種形式,介紹如下:
contentType:"application/json",即向服務器發送一個json字符串。 注意:contentType:"application/json"一旦設定,data必須是json字符串,不能是json對象
這種類型,使用request.POST 無法顯示,這種類型要使用 request.body才能顯示數據。
1.4,text/xml(不常用)
相比於JSON,XML不能更好的適用於數據交換,它包含了太多的包裝,而且它跟大多數編程語言的數據模型不匹配,讓大多數程序員感到詫異,XML是面向數據的,JSON是面向對象和結構的,后者會給程序員一種更加親切的感覺。
我們現在一般這樣來使用:
- 1,XML存儲數據,存儲配置文件等需要結構化存儲的地方使用;
- 2,數據傳輸,數據交互使用JSON
下面就是 ajax Content-Type 為 text/xml 的請求:

2,文件上傳
注意:我們使用表單上傳文件時,必須讓表單的enctype 等於 multipart/form-data。
2.1 基於form表單的文件上傳
html代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload</title>
</head>
<body>
{# 文件上傳的form#}
{#<form method="post" enctype="multipart/form-data">#}
<form method="post" action="/user8_book/upload.html/" enctype="multipart/form-data">
{% csrf_token %}
<p><input id="name" type="text" name="input-name" placeholder="請輸入文件名稱" ></p>
<p><input id="file" type="file" name="file"></p>
<p><input id="submit" type="submit" value="submit"></p>
</form>
</body>
</html>
view視圖函數:
from django.shortcuts import render, HttpResponse
import os
# Create your views here.
def index(request):
if request.method == 'POST':
obj = request.FILES.get('file')
print("獲取文件的名稱: ", obj.name)
f = open(os.path.join('statics/upload', obj.name), 'wb')
for line in obj.chunks():
f.write(line)
f.close()
# POST請求數據
print("POST 請求數據: ", request.POST)
# GET 請求數據
print("上傳的文件數據: ", request.FILES)
return HttpResponse("upload OK")
return render(request, 'file_put/index.html')
我們上傳數據后,在后台終端可以看到下面信息:
獲取文件的名稱: distance.jpg
POST 請求數據: <QueryDict: {'csrfmiddlewaretoken':
['uHoRIy1DrQyS2nD87UndwfoQXH9KEd5zEcIpzjZPjo2zk84TfBatR9QCbaAmckFh'],
'user': ['file1']}>
上傳的文件數據: <MultiValueDict: {'avatar': [<InMemoryUploadedFile: distance.jpg
(image/jpeg)>]}>
2.2 基於AJAX文件上傳
html代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"></script>
</head>
<body>
<h2>基於AJAX文件上傳</h2>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>username:<input type="text" name="user" placeholder="請輸入文件名稱"></p>
<p>photo:<input type="file" name="avatar" ></p>
<p><input type="submit" value="ajax submit" class="btn"></p>
</form>
</body>
<script>
// ajax上傳
$('.btn').click(function () {
var formdata = new FormData();
formdata.append('user', $("#user").val());
formdata.append('avatar', $("#avatar")[0].files[0]);
$.ajax({
url: '',
type: 'post',
contentType: false,
processData: false,
data: formdata,
success: function (data){
console.log(data)
}
})
})
</script>
</html>
注意1:使用ajax上傳文件的時候,前端代碼必須出現下面:
contentType: false,
processData: false,
注意2:AJAX(Formdata)是什么呢?
XMLHttpRequest Level 2 添加了一個新的接口 FormData,利用FormData對象,我們可以通過JavaScript用一些鍵值對來模擬一系列表單控件,我們還可以使用XMLHttpRequest 的 send() 方法來異步的提交這個“表單”。比起普通的 ajax,使用FormData的最大優點就是我們可以異步上傳一個二進制文件。
所有主流瀏覽器的較新版本都已經支持這個對象了。比如Chtome 7+, Firefox 4+, IE 10+,Opera 12+,Safari 5+ 等。
view函數:
from django.shortcuts import render, HttpResponse
import os
# Create your views here.
def index(request):
if request.method == 'POST':
obj = request.FILES.get('avatar')
print("獲取文件的名稱: ", obj.name)
f = open(os.path.join('statics/upload', obj.name), 'wb')
for line in obj.chunks():
f.write(line)
f.close()
# POST請求數據
print("POST 請求數據: ", request.POST)
# GET 請求數據
print("上傳的文件數據: ", request.FILES)
return HttpResponse("upload OK")
return render(request, 'file_put/index.html')
def ajax_put(request):
if request.method == 'POST':
obj = request.FILES.get('avatar')
print("獲取文件的名稱: ", obj)
# POST請求數據
print("POST 請求數據: ", request.POST)
# 文件的請求數據
print("上傳的文件數據: ", request.FILES)
f = open(os.path.join('statics/upload', obj.name), 'wb')
for line in obj.chunks():
f.write(line)
f.close()
return HttpResponse("upload OK")
# if request.method == 'POST':
# # 請求報文中的請求體
# print(request.body)
# print(request.POST)
# print(request.FILES)
# file_obj = request.FILES.get('avatar')
# with open(file_obj.name, 'wb') as f:
# for line in file_obj:
# f.write(line)
# return HttpResponse("AJAX OK")
return render(request, 'file_put/ajax_index.html')
我們上傳數據后,在后台終端可以看到下面信息:
獲取文件的名稱: 50788990.jpg
POST 請求數據: <QueryDict: {'csrfmiddlewaretoken':
['x1KicPbnZ6k7lg7AeerN4WBfUC14JLeoHw4Q3A9zREOOD1ylmVe3pQ3185sGhSO6'],
'user': ['file132']}>
上傳的文件數據:
<MultiValueDict: {'avatar': [<InMemoryUploadedFile: 50788990.jpg (image/jpeg)>]}>
Form組件學習
1,什么是forms組件
forms組件就是一個類,可以檢查前端傳來的數據,是否合法。
例如前端傳來的郵箱數據,判斷郵箱格式是否正確,用戶名不能以什么開頭等等。
2,form組件的使用語法
簡單舉個例子說一下:
from django.shortcuts import render, HttpResponse
from django import forms
# 1.先寫一個類,繼承Form
class MyForm(forms.Form):
# 定義一個屬性,可以用來校驗字符串類型
# 限制最大長度是8,最小長度是3
name=forms.CharField(max_length=8,min_length=3)
pwd=forms.CharField(max_length=8,min_length=3,required=True)
# 校驗是否是郵箱格式
email=forms.EmailField()
# 2.在視圖函數中使用MyForm來校驗數據
# 實例化產生對象,傳入要校驗的數據(可以傳字典字典,也可以不傳)
myform=MyForm(request.POST)
# 3.校驗,is_valid如果是true表示校驗成功(滿足myform里的條件),反之,校驗失敗
if myform.is_valid():
# myform.clean_data 表示校驗通過的數據
print(myform.cleaned_data)
return HttpResponse('校驗成功')
else:
print(myform.cleaned_data)
#校驗失敗的信息,myform.errors 可以當成一個字典,它是所有錯誤信息{name:[列表,]}
# 每個字段.errors 是一個列表,表示每個字段的錯誤信息
print(myform.errors)
return HttpResponse('校驗失敗')
方法總結:
- myform.clean_data 驗證通過的數據
- myform.errors 錯誤數據的對象
- myform.errors.as_data 錯誤數據的信息
3,渲染模板
form組件可以在視圖函數中使用,也可以在前端模板中使用。我們展示一下視圖層和模板層。
視圖層:
# 視圖層:
def index(request):
myform = Myform()
return render(request,'index.html',local())
模板層:
# 模板層
# 1.渲染方式一:
<form action='' method='post'>
用戶名:{{myform:name}} <br>
<input type='submit' value = '提交'></input>
</form>
# 這里的{{myform:name}} 和你寫input框是一樣的效果,就是屬性比input框多一點
# 2.渲染方式二(推薦使用):
<form action='' method='post'>
{% for foo in myform%}
{{ foo.lable }} : {{ foo }} <br>
<input type='submit' value = '提交'></input>
</form>
# 頁面顯示都是一樣的,foo.lable不是用戶名,是name,但是可以在創建Myform類時,在CharFiel中添加lable='用戶名',這樣就行了。
# 3.渲染方式三:
<form action='' method='post'>
{{ myform.as_p }}
<input type='submit' value = '提交'></input>
</form>
下面舉3個例子,詳細說一下這三種渲染方式
3.1 form組件的渲染方式1
html代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>username: <input type="text" name="name"></p>
<p>password:<input type="text" name="pwd"></p>
<p>re_password:<input type="text" name="r_pwd"></p>
<p>email:<input type="text" name="email"></p>
<p>telephone: <input type="text" name="tel"></p>
<p><input type="submit"></p>
</form>
<hr>
<h3>渲染方式1 form組件的渲染</h3>
<form action="" method="post">
{% csrf_token %}
<p>username:
{{ form.name }}
</p>
<p>password:
{{ form.pwd }}
</p>
<p>re_password:
{{ form.r_pwd }}
</p>
<p>email:
{{ form.email }}
</p>
<p>telephone:
{{ form.tel }}
</p>
<p><input type="submit"></p>
</form>
</body>
</html>
views.py
from django.shortcuts import render,HttpResponse
# Create your views here.
from django import forms
class UserForm(forms.Form):
name = forms.CharField(min_length=3)
pwd = forms.CharField(min_length=3)
r_pwd = forms.CharField(min_length=3)
email = forms.EmailField()
# 手機號碼的規則固定為11位,***
tel = forms.CharField(min_length=3)
def reg(request):
if request.method == 'POST':
# print(request.POST)
# form = UserForm({'name': 'yuan', 'email': '123'})
# print(form.is_valid())
form = UserForm(request.POST)
print(form.is_valid())
if form.is_valid():
print(form.cleaned_data)
else:
print(form.errors)
return HttpResponse("OK")
form = UserForm()
return render(request, 'form/reg.html', locals())
3.2 form組件渲染方式2
如果變量太多了,我們不可能這樣寫很多個,所以我們使用for循環。
<hr>
<h3>渲染方式2 form組件的渲染</h3>
<form action="" method="post">
{% csrf_token %}
{% for foo in form %}
<div>
<label for="">{{ foo.label }}</label>
{{ foo }}
</div>
{% endfor %}
<p><input type="submit"></p>
</form>
我們可以在form函數里面設置label標簽:
class UserForm(forms.Form):
name = forms.CharField(min_length=3, label='用戶名')
pwd = forms.CharField(min_length=3, label='密碼')
r_pwd = forms.CharField(min_length=3, label='確認密碼')
email = forms.EmailField( label='郵箱')
# 手機號碼的規則固定為11位,***
tel = forms.CharField(min_length=3, label='電話')
3.3 form組件渲染方式3
<hr>
<h3>渲染方式3 form組件的渲染</h3>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<p><input type="submit"></p>
</form>
但是這種方式不推薦使用。因為其固定死了格式。簡單測試的時候可以使用。
三種效果都一樣,結果也是,這里就不再展示了。我們推薦使用第二種渲染方式。
4,form組件渲染錯誤信息
當返回請求的數據失敗的時候,我們如何渲染錯誤信息
<form action='' method='post'>
{% for foo in myform%}
{{ foo.lable }} : {{ foo }} <span>{{foo.errors.0}}</span><br>
<input type='submit' value = '提交'></input>
</form>
舉個例子:
視圖函數:
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干凈的字段以及對應的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校驗錯誤的字段":["錯誤信息",]}
print(form.errors.get("name")) # ErrorList ["錯誤信息",]
return render(request,"register.html",locals())
form=UserForm()
return render(request,"register.html",locals())
模板:
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }} <span class="pull-right" style="color: red">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit" class="btn btn-default">
</form>
5,Form組件校驗的局部鈎子和全局鈎子
5.1,什么是局部鈎子
定義一個函數,名字叫:clean_字段名字,內部,取出該字段,進行校驗。如果通過,將該字段返回,如果失敗,拋出異常(ValidationError)。
其中ValidationError的異常類型需要引入:
from django.core.exceptions import ValidationError
5.2,局部鈎子的源碼分析
首先,我們分析一段源碼。關於鈎子函數:

從is_valid()點進去,然后點擊errors:(也就是 form/forms.py的源碼)
我們可以看到下面:

點進去full_clean(),然后查看_clean_filed_() 方法
注意(看源碼訣竅):看源碼的時候,看不懂就過!!

這里我們查看_clean_fields()函數,這是局部鈎子的應用

首先,從fileds字段中導入其內容,當沒有問題的時候,嘗試判斷其是否合法,如果合法的話,我們將vlaue字段賦值給 cleaned_data[name],然后利用一個反射函數,嘗試調用局部鈎子,如果有局部鈎子函數,我們調用並執行,如果校驗成功,我們將name 的值返回到clean_data,並寫入clean_data字典中,也就是更新字典,如果出錯的話,就拋出異常,將異常信息以鍵值對({'name': value} 寫入 errors 字典中)。
如果出錯的話,局部鈎子拋出的異常會添加到該字段中的錯誤信息,我們在前台獲取錯誤信息的方法如下:
for循環生成 input 框
{{ foo.errrs.0 }}
5.3,局部鈎子示例:
# 函數名稱必須以 claen_字段名 的格式
def clean_name(self):
# 目的:如果用戶名已經注冊,則報異常
val = self.cleaned_data.get('name')
ret = UserInfo.objects.filter(name=val)
if not ret:
return val
else:
raise ValidationError('用戶名已經被注冊')
def clean_tel(self):
# 目的:校驗手機號碼長度為11
val = self.cleaned_data.get('tel')
if len(val) == 11:
return val
else:
raise ValidationError("手機號碼格式錯誤")
5.4,什么是全局鈎子
在寫注冊用戶的時候,有輸入密碼,確認密碼,可以進行布局鈎子處理,處理完畢是不是在進行判斷,判斷其是否相等,相等的話就存到數據庫中,不相等就拋出異常。
全局鈎子主要應用場景就是每次校驗多個字段,而局部鈎子每次取的是單個字段,單個變量。
5.5,全局鈎子的源碼分析
下面繼續分析一段源碼。

從cleaned_data() 點擊進去,我們會發現 clean_form執行的方法是 clean()方法。
下面我們看clean()方法:

我們會發現 默認的clean()方法什么都沒有寫,直接返回的一個 cleaned_data!!所以這就是給我們寫的,讓我們覆蓋的東西。
為什么這么說呢?
我們會發現,點進去類 UserInfo中 forms.Form繼承基類方法BaseForm。

而我們所寫的全局鈎子中 clean_data也是在基類BaseForm里面。所以代碼會先去我們所寫的類中找clean()方法,如果沒有的話,再繼續執行下一步。
校驗成功的話,和局部鈎子一樣,更新字段值,並返回到clean_data字典里面。但是校驗失敗的話,拋出的異常和局部鈎子有點不一樣,他是將異常信息以鍵值對({'__all__': [value, ]}) 寫入errors字典中。全局鈎子拋出的異常會添加到__all__里面,所以我們在后台獲取異常信息
后台獲取錯誤信息是這樣的:
errors = form.errors.get('__all__')
myforms.errors.get('__all__')[0]
注意先判斷上面的 errors 是否存在,存在的話,在前台獲取錯誤信息如下:
{{ myforms.errors.__all__.0 }}
5.6,全局鈎子示例:
# 函數名稱必須命名為clean
def clean(self):
pwd = self.cleaned_data.get('pwd')
r_pwd = self.cleaned_data.get('r_pwd')
# 首先判斷是否都通過檢測,都不為空,再進行校驗
if pwd and r_pwd:
if pwd == r_pwd:
# 如果兩次密碼相同,則返回干凈的字典數據
return self.cleaned_data
else:
# 沒通過檢測,則返回異常信息
ValidationError('兩次密碼不一致')
else:
# 如果獲取的兩個變量中,但凡有一個為空,我們就不需要校驗了
return self.cleaned_data
5.7,一個完整的forms組件校驗
這里代碼主要展示了視圖層和form組件和HTML代碼。當然前提是我們需要有UserInfo這個數據庫。算了還是展示一下models函數吧
models.py
from django.db import models
# Create your models here.
class UserInfo(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
email = models.EmailField()
tel = models.CharField(max_length=32)
MyForms.py
from django import forms
from django.forms import widgets
from form_demo.models import UserInfo
from django.core.exceptions import ValidationError
class UserForm(forms.Form):
name = forms.CharField(min_length=3, label='用戶名',
error_messages={'required': '用戶名最短是3位!!'},
widget=widgets.TextInput(attrs={'class': 'form-control'}))
pwd = forms.CharField(min_length=3, label='密碼',
error_messages={'required': '密碼最短是3位!!'},
widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
r_pwd = forms.CharField(min_length=3, label='確認密碼',
error_messages={'required': '密碼最短是3位!!'},
widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(label='郵箱',
error_messages={'required': '不符合郵箱格式!!'},
widget=widgets.TextInput(attrs={'class': 'form-control'}))
# 手機號碼的規則固定為11位,***
tel = forms.CharField(min_length=3, label='電話',
widget=widgets.TextInput(attrs={'class': 'form-control'}))
# 函數名稱必須以 claen_字段名 的格式
def clean_name(self):
# 目的:如果用戶名已經注冊,則報異常
val = self.cleaned_data.get('name')
ret = UserInfo.objects.filter(name=val)
if not ret:
return val
else:
raise ValidationError('用戶名已經被注冊')
def clean_tel(self):
# 目的:校驗手機號碼長度為11
val = self.cleaned_data.get('tel')
if len(val) == 11:
return val
else:
raise ValidationError("手機號碼格式錯誤")
# 函數名稱必須命名為clean
def clean(self):
pwd = self.cleaned_data.get('pwd')
r_pwd = self.cleaned_data.get('r_pwd')
# 首先判斷是否都通過檢測,都不為空,再進行校驗
if pwd and r_pwd:
if pwd == r_pwd:
# 如果兩次密碼相同,則返回干凈的字典數據
return self.cleaned_data
else:
# 沒通過檢測,則返回異常信息
ValidationError('兩次密碼不一致')
else:
# 如果獲取的兩個變量中,但凡有一個為空,我們就不需要校驗了
return self.cleaned_data
views.py
from django.shortcuts import render, HttpResponse, redirect
# Create your views here.
from form_demo.Myforms import UserForm
def reg(request):
if request.method == 'GET':
form = UserForm()
return render(request, 'form/reg.html', locals())
elif request.method == 'POST':
# form 表單的 name 屬性值應該與forms組件字段名稱一致
form = UserForm(request.POST)
print(form.is_valid())
# print(forms.clean)
if form.is_valid():
print(form.cleaned_data)
# 校驗全部通過,創建數據時,從clean_data中獲取數據,
# 但是必須將其中多於的數據pop掉,如下面代碼
# myform.cleaned_data.pop('re_pwd')
# models.User.objects.create(**myform.cleaned_data)
return redirect('http://www.baidu.com')
else:
all_error = form.errors.get('__all__')
if all_error:
all_error = all_error[0]
print(form.errors)
print(form.errors.get('__all__'))
# return render(request, 'form/reg.html', locals())
return render(request, 'form/reg.html', locals())
reg.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<hr>
<hr>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form action="" method="post">
{% csrf_token %}
<p><label>{{ form.name.label }}</label>
{{ form.name }}<span class="pull-right error">{{ form.name.errors.0 }}</span>
</p>
<p><label>{{ form.pwd.label }}</label>
{{ form.pwd }}<span class="pull-right error">{{ form.pwd.errors.0 }}</span>
</p>
<p><label>{{ form.r_pwd.label }}</label>
{{ form.r_pwd }}<span class="pull-right error">{{ form.r_pwd.errors.0 }}</span><span>{{ all_error }}</span>
</p>
<p><label>{{ form.email.label }}</label>:
{{ form.email }}<span class="pull-right error">{{ form.email.errors.0 }}</span>
</p>
<p><label>{{ form.tel.label }}</label>:
{{ form.tel }}<span class="pull-right error">{{ form.tel.errors.0 }}</span>
</p>
<p><input type="submit"></p>
</form>
</div>
</div>
</div>
</body>
</html>
我們展示一下效果。
1,我們給數據庫存入james這個名字,然后注冊james,我們在前端看效果:

2,我們注冊電話號碼為5位,我們在前端看效果:
3,我們注冊密碼和確認密碼不一致的時候,我們在前端看效果:

參考文獻:https://www.cnblogs.com/JetpropelledSnake/p/9397889.html#top(局部鈎子和全局鈎子,本來我放的代碼,但是百度找到了這位老鐵畫的圖的,就借花獻佛,直接拿來用了,自己再加以詳細的解釋)
https://www.cnblogs.com/yuanchenqi/articles/7638956.html

