Django框架之八 form元件 鉤子函式
阿新 • • 發佈:2020-10-25
Django框架之八 form元件 鉤子函式
一、什麼是form元件,可以幹什麼
1.forms元件就是一個類,可以檢測前端傳來的資料,是否合法。 例如,前端傳來的郵箱資料,判斷郵件格式對不對,使用者名稱中不能以什麼開頭,等等 >>>校驗資料 2.還可以前端頁面搭建 >>> 渲染頁面 3.展示錯誤資訊 >>> 渲染錯誤資訊
二、form元件的使用
1.使用語法
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) #校驗郵箱 email = forms.EmailField() #2.在檢視函式中使用MyForm來校驗資料 #例項化產生物件,將需要校驗的資料傳入(可以傳字典,可以不傳),這個資料是前端傳遞過來的資料myform = MyForm(request.POST) #3.校驗,is_valid如果是true表示校驗成功(滿足myform裡面的所有條件),反之,驗證失敗 if myform.is_valid(): print(myform.cleaned_data) #列印校驗通過的資料 return HttpResponse('校驗成功') else: #校驗失敗的資訊 是一個字典,它是所有錯誤資訊 {錯誤欄位名:[錯誤原因]} print(myform.errors) #errors是一個列表,表示每個欄位的錯誤資訊 return HttpResponse('校驗失敗')
方法總結:
clean_data 驗證通過的資料 errors 錯誤資料的物件 errors.as_data 錯誤資料的資訊
注意事項
1.自定義的類中所有的欄位預設都是必須要傳值的
2.可以額外傳入類中沒有定義的欄位名,forms元件不會去校驗,也就是多傳沒有關係
3.例項化時,傳入必須是字典,也可以不傳
form_obj = views.LoginForm({'username':'jason','password':'123456','email':'[email protected]'}) form_obj.is_valid() True form_obj = views.LoginForm({'username':'jason','password':'123456'}) form_obj.is_valid() Falseform_obj = views.LoginForm({'username':'jason','password':'123456','email':'[email protected]','hobby':'read'}) form_obj.is_valid() True
2.元件的引數及其他操作方式
max_length #代表該欄位最長可以為多少
min_length #代表該欄位最短可以為多少
error_messages #設定錯誤資訊的屬性
required #預設值為True,意思是你前端傳來的欄位必須有它,沒有的話校驗失敗
label #註釋資訊label ='使用者名稱' 在前端渲染可以直接物件.label獲取值
initial #設定預設值
widget #控制標籤屬性和樣式
widget = widgets.PasswordInput() #你在模板渲染的時候,就會渲染成Input框,password樣式
控制標籤屬性
widget = widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'})
#例子
pwd = forms.CharField(max_length=8,min_length=3,required=True,label='密碼',
error_messages = {'max_length':'最長是8','min_length':'最短是3','required':'這個必須填'}
)
其他操作方式
# 單選的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()
)
三、渲染頁面、渲染錯誤資訊
#form元件可以在檢視函式中使用,也可以在模板中使用
#檢視層
def reg(request):
myform = MyForm()
if request.method == 'POST':
myform = MyForm(request.POST)
print(request.POST) #<QueryDict: {'name': ['小張'], 'pwd': ['123123'], 'email': ['[email protected]']}>
return render(request,'reg.html',locals()) #把myform物件傳到前端頁面了
#模板層 #第一種渲染方式(封裝程度太高,一般只用於本地測試,通常不適用) <form action='' method='post'> {{ myform.as_p }} {{ myform.as_ul }} {{ myform.as_table}} <input type='submit' value = '提交'></input> </form> #第二種渲染頁面的方式(可擴充套件性高,書寫麻煩) <form action='' method='post'> {{myform.name.label}}:{{myform:name}} {{myform.pwd.label}}:{{myform:pwd}} {{myform.email.label}}:{{myform:email}} <input type='submit' value = '提交'></input> </form> #第三種渲染方式(推薦) <form action='' method='post'> {% for foo in myform %} <p> {{ foo.lable }} : {{ foo }} <span>{{ foo.errors.0 }}</span> //注意後面加0 錯誤資訊 </p> <input type='submit' value = '提交'></input> </form> 複製程式碼
注意事項:
1.forms元件在幫你渲染頁面的時候 只會渲染獲取使用者輸入的標籤 提交按鈕需要你手動新增 2.input框的label註釋 不指定的情況下 預設用的類中欄位的首字母大寫
四、鉤子函式(HOOK)
forms元件暴露給使用者,可以自定義的校驗規則 用法:在自定義的form類中定義一個函式即可
1.區域性鉤子(針對某一個欄位做額外的校驗)
名字叫:clean_欄位名,內部,取出該欄位進行校驗,如果通過,將該欄位返回,如果失敗,拋異常
函式名:clean_欄位名字 def clean_name(self): #self是當前form物件 name = self.cleaned_data.get('name') #從驗證通過的資料中再次篩選 if '666' in username: #失敗,拋異常,把異常資訊以('key','value')形式寫入add_error中 self.add_error('name','不能包含666') #正常,把name返回到clean_data,將name寫入clean_data字典中 return name
注意點:
- 這些函式是寫在自定義類form裡面的,需要校驗哪個欄位就以 clean_欄位名寫一個校驗函式
- 校驗失敗,通過add_error('欄位名','錯誤資訊'),以{key:value}形式寫入到errors字典中
- 校驗成功,返回name到clean_data,寫入clean_data字典中
2.全域性鉤子(針對多個欄位做額外的校驗)
#重寫clean方法 def clean(self): #能走到這步,說明前面的校驗已經通過了,下面校驗兩次兩次密碼是否輸入一致 pwd = self.clean_data.get('pwd') re_pwd = self.clean_data.get('re_pwd') if pwd == re_pwd: #校驗成功,直接返回clean_data return self.clean_data else: #校驗失敗,把錯誤資訊放入errors中 self.add_error('re_pwd','兩次密碼輸入不一致') 或者 #丟擲異常 raise ValidationError('兩次密碼不一致')
注意點:
- 校驗失敗,有兩種寫法,拋異常,將異常資訊以{'__all__':[value,]}寫入errors字典中
- 校驗成功,返回clean_data字典
- 丟擲異常型別為 ValidationError, from django.core.exceptions import ValidationError匯入
鉤子錯誤資訊渲染注意點:
- 區域性鉤子丟擲的異常會新增到該欄位中的錯誤資訊中,獲取錯誤資訊
前端: for迴圈物件 {{ foo.errors.0}}
- 全域性鉤子丟擲的異常會新增到__all__中,獲取錯誤資訊:
後端: myform.errors.get('__all__')[0] 注意先判斷myform.errors.get('__all__')是否存在
前端:{{ myform.errors.__all__.0}}
- 能走到這些函式,說明前面的form元件的那些校驗都成功了,此時就可以從clean_data中取資料,因為此時clean_data中的資料全部符合,都找的到,而且clean_data是字典比較好取值。
五、完整的form元件校驗
1.檢視層
from django.shortcuts import render, HttpResponse, redirect # forms元件資料校驗的功能 # 第一步:先要繼承Form from django import forms from django.forms import widgets from django.core.exceptions import ValidationError # 寫一個類 class MyForm(forms.Form): # 定義一個屬性,可以用來校驗字串型別 # 限制最大長度是8,最小長度是3 name = forms.CharField(max_length=8, min_length=3, label='使用者名稱' error_messages={'max_length': '最長是8', 'min_length': '最短是3', 'required': '這個必須填'}, widget=widgets.TextInput(attrs={'class': 'form-control'})) pwd = forms.CharField(max_length=8, min_length=3, required=True, label='密碼', error_messages={'max_length': '最長是8', 'min_length': '最短是3', 'required': '這個必須填'}, widget=widgets.PasswordInput()) re_pwd = forms.CharField(max_length=8, min_length=3, required=True, label='確認密碼', error_messages={'max_length': '最長是8', 'min_length': '最短是3', 'required': '這個必須填'}, widget=widgets.PasswordInput()) # 校驗是否是郵箱格式 email = forms.EmailField(label='郵箱', error_messages={'required': '這個必須填', 'invalid': '不符合郵箱格式'}) # aa = forms.CharField(label='選擇', error_messages={'required': '這個必須填', 'invalid': '不符合郵箱格式'},widget=widgets.CheckboxInput()) def clean_name(self): # self:當前form物件 name = self.cleaned_data.get('name') if name.startswith('sb'): # 失敗,拋異常 raise ValidationError('不能以傻逼開頭') # 正常,把name返回 return name def clean(self): pwd = self.cleaned_data.get('pwd') re_pwd = self.cleaned_data.get('re_pwd') if pwd == re_pwd: return self.cleaned_data else: raise ValidationError('兩次密碼不一致') def index_form(request): # 生成物件時(例項化),需要傳入要校驗的資料(字典) if request.method == 'GET': myform = MyForm() return render(request,'indxe2.html',locals()) elif request.method == 'POST': myform = MyForm(request.POST) if myform.is_valid(): # print(myform.cleaned_data) # 驗證通過的資料 # models.User.objects.create(name='lqz',pwd='123',re_pwd='123) myform.cleaned_data.pop('re_pwd') models.User.objects.create(**myform.cleaned_data) return redirect('http://www.baidu.com') else: all_error = myform.errors.get('__all__') if all_error: all_error = all_error[0] # print(myform.errors.as_data) return render(request, 'indxe3.html', locals()) 複製程式碼
2.模板層
<form action="" method="post" novalidate> {% for foo in myform %} <p>{{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span></p> {% endfor %} <input type="submit" value="提交"><span>{{ all_error }}</span> </form>