1. 程式人生 > 實用技巧 >Django框架之八 form元件 鉤子函式

Django框架之八 form元件 鉤子函式

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()
False
form_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>