Forms組件與鉤子函數

一:Forms組件

1.案例需求:
寫一個註冊功能
1.獲取用戶名和密碼 利用form表單提交數據
2.在後端判斷用戶名和密碼是否符合一定的條件
	3.用戶名中不能含有金眉瓶
	4.密碼不能少於三位
	
# 符合條件需要你將提示資訊動態的展示到前端頁面	
2.前端
<form action="" method="post">
    <p>username:
        <input type="text" name="username">
        {# 行內標籤 get請求為空 不佔任何標籤    post請求有值 就可以點到對應的數據    #}
        <span style="color: red">{{ back_dic.username }}</span>
    </p>
    <p>password:
        <input type="text" name="password">
        <span style="color: red">{{ back_dic.password }}</span>
    </p>
    <input type="submit" class="btn btn-info">
</form>
3.後端
1.無論是post請求還是get請求
	頁面都能夠獲取到字典 只不過get請求來的時候 字典值都是空的
2.而post請求來之後 字典可能有值
def ab_form(request):
    # 定義一個字典 下面替換的方式
    back_dic = {'username':'', 'password':''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if '金眉梅' in username:
            back_dic['username'] = '不符合社會主義核心價值觀!'
        if len(password) < 3:
            back_dic['password'] = '不能太短 不好!'
    return render(request, 'ab_form.html', locals())

image

二:form表單前後端動態交互

1.手動書寫前端獲取用戶數據的html程式碼								渲染html程式碼
2.後端對用戶數據進行校驗											 校驗數據
3.對不符合要求的數據進行前端提示									展示提示資訊
1.form組件
# forms組件
能夠完成的事情
	1.渲染html程式碼
	2.校驗數據
	3.展示提示資訊
2.為什麼數據效驗非要去後端 不能在前端利用js直接完成呢?
必記!
1.數據效驗前端可有可無
2.但是後端必須要有!

原因:
	1.因為前端的效驗是弱不禁風的 你可以直接修改
	2.或者利用爬蟲程式繞過前端頁面直接朝後端提交數據
3.舉例:購物網站
1.選取了貨物之後 會計算一個價格發送給後端 如果後端不做價格的效驗 會造成被別人篡改!

實際情況:
	1.實際是獲取到用戶選擇的所有商品主鍵值
	2.然後在後端查詢出所有商品的價格 再次計算一遍
	3.如果前端一致 那麼完成支付如果不一致直接拒絕

三:基本使用

1.作用於校驗型組件
# views.py

from django import forms

class MyForm(forms.Form):
    # username字元串類型最小3位最大8位
    username = forms.CharField(min_length=3, max_length=8)
    # password字元串類型最小3位最大8位
    password = forms.CharField(min_length=3, max_length=8)
    # email欄位必須符合郵箱哥哥是  [email protected]
    email = forms.EmailField()
2.校驗數據
3.測試環境兩種方式
1.測試環境的準備 可以自己拷貝程式碼準備
2.其實在pycharm左下角已經幫你準備一個測試環境  "python console"

image

from app01 import views
# 1 將帶校驗的數據組織成字典的形式傳入即可
form_obj = views.MyForm({'username':'jason','password':'123','email':'123'})

# 2 判斷數據是否合法		注意該方法只有在所有的數據全部合法的情況下才會返回True
form_obj.is_valid()
False

image

# 3 查看所有校驗通過的數據
form_obj.cleaned_data
{'username': 'jason', 'password': '123'}

image

# 4 查看所有不符合校驗規則以及不符合的原因
form_obj.errors
{
  'email': ['Enter a valid email address.']
}

image

# 5 校驗數據只校驗類中出現的欄位 多傳不影響 多傳的欄位直接忽略
form_obj = views.MyForm({'username':'jason','password':'123','email':'[email protected]','hobby':'study'})
form_obj.is_valid()
True
# 6 校驗數據 默認情況下 類裡面所有的欄位都必須傳值
form_obj = views.MyForm({'username':'jason','password':'123'})
form_obj.is_valid()

False

image

總結:

也就意味著校驗數據的時候 默認情況下數據可以多傳但是絕不可能少傳

四:渲染標籤

froms組件只會自動幫你渲染獲取用戶輸入的標籤(input select radio checkbox)

不能幫你渲染提交按鈕!
1.後端(生成一個空對象)
def index(request):
    # 1 先產生一個空對象
    form_obj = MyForm()
    # 2 直接將該空對象傳遞給html頁面
    return render(request,'index.html',locals())
2.前端利用空對象做操作
3.第一種渲染方式
  • 第一種渲染方式:程式碼書寫極少 封裝程度太高 不便於後續的擴展 一般情況下只在本地測試使用
<form action="" method="post">
    <p>第一種自動渲染欄位與input框方式</p>
    {{ form_obj.as_p }}  // p標籤
    {{ form_obj.as_ul }}  
    {{ form_obj.as_table }}  

image

4.第二種渲染方式
  • 可擴展性很強 但是需要書寫的程式碼太多 一般情況下不用
<p>第二種渲染方式: 可擴展性很強 但是需要書寫的程式碼太多 一般情況下不用</p>
<p>{{ form_obj.username.label }}:{{ form_obj.username }}</p>
<p>{{ form_obj.password.label }}:{{ form_obj.password }}</p>
<p>{{ form_obj.email.label }}:{{ form_obj.email }}</p>

.label 			: 拿到前面的框對應的注釋資訊

image

5.第三種渲染方式
  • 第三種渲染方式::程式碼書寫簡單 並且擴展性也高
<p>第三種渲染方式(推薦使用)</p>
{% for form in form_obj %}
	<p>{{ form.label }}:{{ form }}</p>
{% endfor %}

image

6.label屬性作用
label屬性默認展示的是類中定義的欄位首字母大寫的形式
也可以自己修改 直接給欄位對象加label屬性即可

username = forms.CharField(min_length=3,max_length=8,label='用戶名')

五:展示提示資訊

1.渲染錯誤資訊
def index(request):
    # 1.先產生一個空對象
    form_obj = MyForm()
    if request.method == 'POST':
        # 獲取用戶數據並且效驗
        """
        1.數據獲取繁瑣
        2.效驗數據需要構造成字典的格式傳入才行
        ps: 但是request.POST可以看成就是一個字典
        """
        # 3,效驗數據
        form_obj = MyForm(request.POST)
        # 4.判斷數據是否合法
        if form_obj.is_valid():
            # 5.如果合法操作資料庫存儲數據
            return HttpResponse('OK')
        # 5.如果不合法 有錯誤

    # 2.直接將該空對象傳遞給html頁面
    return render(request, 'index.html', locals())


# index.html
<form action="" method="post">

    <p>第三種渲染方式(推薦使用): 程式碼書寫簡單 並且擴展性也高</p>
    {% for form in form_obj %}
        <p>
            {{ form.label }}:{{ form }}
            <span style="color: red">{{ form.errors }}</span>
        </p>
    {% endfor %}
    <input type="submit" class="btn btn-info">
</form>

image

2.如何讓瀏覽器不做校驗
<form action="" method="post" novalidate>

image

3.forms組件展示錯誤資訊(必備條件)
1.get請求和post傳給html頁面對象變數名(form_obj)必須一樣
2.forms組件當你的數據不合法的情況下 會保存你上次的數據 讓你基於之前的結果進行修改
更加的人性化
def index(request):
    # 1.先產生一個空對象
    form_obj = MyForm()
    if request.method == 'POST':
        # 獲取用戶數據並且效驗
        """
        1.數據獲取繁瑣
        2.效驗數據需要構造成字典的格式傳入才行
        ps: 但是request.POST可以看成就是一個字典
        """
        # 3,效驗數據
        form_obj = MyForm(request.POST)
        # 4.判斷數據是否合法
        if form_obj.is_valid():
            # 5.如果合法操作資料庫存儲數據
            return HttpResponse('OK')
        # 5.如果不合法 有錯誤

    # 2.直接將該空對象傳遞給html頁面
    return render(request, 'index.html', locals())
4.前端
<form action="" method="post" novalidate>

    <p>第三種渲染方式(推薦使用): 程式碼書寫簡單 並且擴展性也高</p>
    {% for form in form_obj %}
        <p>
            {{ form.label }}:{{ form }}
            <span style="color: red">{{ form.errors.0 }}</span>
        </p>
    {% endfor %}
    <input type="submit" class="btn btn-info">
</form>

image

5.針對錯誤的提示資訊可以自訂製
  • 自訂製提示資訊作用
如果用戶輸入的不符合以下要求,就會根據相應的錯誤進行提示。
class MyForm(forms.Form):
    # username字元串類型最小3位最大8位
    username = forms.CharField(min_length=3,max_length=8,label='用戶名',
                               error_messages={
                                   'min_length':'用戶名最少3位',
                                   'max_length':'用戶名最大8位',
                                   'required':"用戶名不能為空"
                               }
                               )
    # password字元串類型最小3位最大8位
    password = forms.CharField(min_length=3,max_length=8,label='密碼',
                               error_messages={
                                   'min_length': '密碼最少3位',
                                   'max_length': '密碼最大8位',
                                   'required': "密碼不能為空"
                               }
                               )
    # email欄位必須符合郵箱格式  [email protected]
    email = forms.EmailField(label='郵箱',
                             error_messages={
                                 'invalid':'郵箱格式不正確',
                                 'required': "郵箱不能為空"
                             }
                             )

image

六:forms組件其他參數及補充知識點

label			: 欄位名
error_messages	 : 自定義報錯資訊
initial			: 默認值
required		: 控制欄位是否必填/可改為False不必填
1.目前問題漏洞
1.欄位沒有form-control
2.針對不同類型的input如何修改
text
password
date
radio
checkbox
3.多個屬性值情況 空格隔開即可
widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})  # 標籤擁有多個樣式類 空格即可

image

4.第一道關卡裡面還支援正則校驗(手機號等…)
from django.core.validators import RegexValidator

phone = forms.CharField(
    validators=[
        RegexValidator(r'^[0-9]+$', '請輸入數字'),
        RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')
    ],
)

七:其他類型渲染

1.radio性別單選
    gender = forms.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性別",
        initial=3,
        widget=forms.widgets.RadioSelect()
        )
2.select愛好多選
hobby = forms.ChoiceField(
        choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),
        label="愛好",
        initial=3,
        widget=forms.widgets.Select()
    )
3.多選checkbox
    hobby2 = forms.MultipleChoiceField(
        choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),
        label="愛好",
        initial=[1, 3], 
        widget=forms.widgets.CheckboxSelectMultiple()
    )

八:鉤子函數(HOOK)

1.鉤子函數的作用
在特定的節點自動觸發完成響應操作
2.什麼是鉤子函數?
鉤子函數在forms組件中就類似於第二道關卡,能夠讓我們自定義校驗規則!
3.在forms組件中有兩類鉤子
1.局部鉤子
	當你需要給單個欄位增加效驗規則的時候可以使用
2.全局鉤子
	當你需要給多個欄位增加效驗規則的時候可以使用

七:實際案例

1.鉤子函數觸發時間:
鉤子函數觸發時間  當第一道關卡走過去後,沒有問題,才執行第二道關卡(鉤子函數)

1.通過第一道關卡的數據會放在self.cleaned_data裡面
2.self就是當前類產生的對象,就是form_obj
2.校驗用戶名中不能含有666 只是校驗username欄位 局部鉤子
    # 局部鉤子
    def clen_username(self):
        # 獲取用戶名
        username = self.cleaned_data.get('username')
        # 判斷666是否再username內
        if '666' in username:
            # 提示前端展示錯誤資訊
            # add_error內 第一個放 要設置的欄位 第二個放 展示的錯誤資訊
            self.add_error('username', '光喊666是不行滴!')
        # 將鉤子函數鉤出去數據再放回去
        return username
3.校驗密碼和確認密碼是否一致 password confirm兩個欄位 全局鉤子
    # 全局鉤子
    def clean(self):
        # 獲取密碼
        password = self.cleaned_data.get('password')
        # 獲取用戶確認密碼
        confirm_password = self.cleaned_data.get('confirm_password')
        # 判斷兩次密碼是否一致
        if not confirm_password == password:
            # 不一致返回
            self.add_error('confirm_password','兩次密碼不一致')
        # 將鉤子函數鉤出來的數據再放回去
        return self.cleaned_data

image