Django Form組件

Django Form組件

查看源圖像

簡介

Django Form 組件有兩大功能,用於對頁面進行初始化,生成 HTML 標籤,此外還可以對用戶提交對數據進行校驗(顯示錯誤信息)

  • 數據重置
  • 校驗規則

form組件和傳統form表單對比

  • 當我們用傳統的form表單提交時會刷新頁面,如果這個我們表單中的某項填錯了,刷新後我們正確的選項也沒有了
  • 傳統的form表單需要我們自己親自校驗每一項,其工作量太大
  • form組件前端自動生成表單元素
  • form組件可自動驗證表單內容信息
  • form組件可保留用戶上次輸入的信息

導入form django import froms

校驗字段

ps:這裡數據量較小使用sqlite3

# settings.py需要修改的配置
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-Hans'  # 修改成中文

# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'   # 時間使用上海的

USE_I18N = True

USE_L10N = True

USE_TZ = False    # 改為當前時區,默認為True

校驗字段實操

我們在不使用forms的情況下也可以校驗用戶註冊的字段長度是否符合標準,比如通過len()等方法,但是過於麻煩,下面通過forms來校驗用戶字段長度;(註冊舉例)

'''
1.註冊頁面,forms校驗,需要定義一個類,來繼承forms.Form
2.自定義類內規定的字段就是校驗規則
3.實例化類,得到form對象,使用is_valid校驗,校驗成功可以通過對象.cleanded.data獲取到乾淨的數據,校驗失敗通過對象.erros返回錯誤信息
'''
需要注意的是,實例化對象要傳入校驗數據!
eg:reg_obj = Reg(data=request.POST)
'''Myforms.py'''
from django import forms

class Register(forms.Form):
    username = forms.CharField(max_length=8, min_length=3, label='用戶名',error_messages={'min_length':'太短了',"required": "該字段不能為空!"})  
    password = forms.CharField(max_length=11, min_length=3, label='密碼')
    re_password = forms.CharField(max_length=11, min_length=3, label='確認密碼')
    email = forms.EmailField(label='郵箱')
  • label:輸入框前面的文本信息。
  • error_message:自定義顯示的錯誤信息,屬性值是字典, 其中 required 為設置不能為空時顯示的錯誤信息的 key
'''views.py'''
from django.shortcuts import render,HttpResponse,redirect
from app01.My_forms import Register

def register(request):
    if request.method == 'GET':
        return render(request,'register.html')
    else:
        # 實例化,傳入校驗數據
        reg_form_obj = Register(data=request.POST)
        # 判斷校驗是否可以通過
        if reg_form_obj.is_valid():
            # 校驗通過存入數據庫
            print('校驗通過')
            print(reg_form_obj.cleaned_data)
            reg_form_obj.cleaned_data.pop('re_password')
            data = reg_form_obj.cleaned_data
            models.Register.objects.create(**data)
			# 將校驗通過的數據打散傳入
        else:
            # 校驗不通過,返回錯誤信息
            print('校驗不通過')
            print(reg_form_obj.errors)
    return HttpResponse('ok')

'''不理解打散可以看下面這幾個示例'''
	# 字符串打散
    s = 'Hammer'
    print(s) # Hammer
    print(*s) # H a m m e r
    # 元組打散
    tup = (1,2,3) 
    print(tup) # (1, 2, 3)
    print(*tup) # 1 2 3
    # 列表打散
    lst = [1,2,3]
    print(lst) # [1, 2, 3]
    print(*lst) # 1 2 3
    # 字典打散
    def func(name,age):
        print(name,age)
    dic = {'name':'Hammer','age':18}
    func(**dic) # Hammer 18    
'''urls.py'''
path('register/', views.register)
'''models.py'''
from django.db import models

class Register(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    email = models.EmailField
<!--register.html-->
<form action="" method="post">
    <div class="container">
        <h1 class="active" style="text-align: center">註冊頁面</h1>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <p>用戶名: <input type="text" name="username" class="form-control"></p>
                <p>密碼: <input type="password" name="password" class="form-control"></p>
                <p>確認密碼: <input type="password" name="re_password" class="form-control"></p>
                <p>郵箱: <input type="email" name="email" class="form-control"></p>
                <input type="submit" value="提交" class="btn btn-block btn-info">
            </div>
        </div>
    </div>
</form>
# 校驗不通過
校驗不通過
<ul class="errorlist"><li>username<ul class="errorlist"><li>該字段不能為空!</li></ul></li></ul>

# 校驗通過
校驗通過
{'username': 'HammerZe', 'password': '123', 're_password': '123', 'email': '[email protected]'}

forms渲染標籤

自己手動寫HTML頁面

<form action="" method="post">
    <div class="container">
        <h1 class="active" style="text-align: center">註冊頁面</h1>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <p>用戶名: <input type="text" name="username" class="form-control"></p>
                <p>密碼: <input type="password" name="password" class="form-control"></p>
                <p>確認密碼: <input type="password" name="re_password" class="form-control"></p>
                <p>郵箱: <input type="email" name="email" class="form-control"></p>
                <input type="submit" value="提交" class="btn btn-block btn-info">
            </div>
        </div>
    </div>
</form>

forms渲染標籤(一)

通過在視圖函數中生成一個空form對象,html頁面可以直接使用該對象進行渲染

def register(request):
    if request.method == 'GET':
        empty_form = Register()
        return render(request,'register.html',{'form':empty_form})
    else:
        # 實例化,傳入校驗數據
        reg_form_obj = Register(data=request.POST)
        # 判斷校驗是否可以通過
        if reg_form_obj.is_valid():
            # 校驗通過存入數據庫
            print('校驗通過')
            print(reg_form_obj.cleaned_data)
        else:
            # 校驗不通過,返回錯誤信息
            print('校驗不通過')
            print(reg_form_obj.errors)
    return HttpResponse('ok')
{#forms渲染標籤1#}
<form action="" method="post">
    <div class="container">
        <h1 class="active" style="text-align: center">註冊頁面2</h1>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <p>用戶名: {{ form.username }}</p>
                <p>密碼: {{ form.password }}</p>
                <p>確認密碼: {{ form.re_password }}</p>
                <p>郵箱: {{ form.email }}</p>
                <input type="submit" value="提交" class="btn btn-block btn-info">
            </div>
        </div>
    </div>
</form>

總結

如果使用forms渲染,前端會優化處理,如果長度超出會自動截取等優點

forms渲染標籤(二)【常用】

標籤頁可以通過for循環form對象來渲染,標籤前面的字段可以通過label屬性來拿到,每循環一次foo就可以得到一個字段

{#forms渲染標籤2#}
<form action="" method="post">
    <div class="container">
        <h1 class="active" style="text-align: center">註冊頁面3</h1>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                {% for foo in form %}
                    <p>{{ foo.label }}:{{ foo }}</p>
                {% endfor %}
                <input type="submit" value="提交" class="btn btn-block btn-info">
            </div>
        </div>
    </div>
</form>

forms渲染標籤(三)

渲染標籤也可以通過一句話來渲染,form.as_p,as_後面可以跟不同標籤的名字,比如as_table,as_ul····,但是這樣渲染標籤直接寫死,擴展性極低!

{#forms渲染標籤3#}
<form action="" method="post">
    <div class="container">
        <h1 class="active" style="text-align: center">註冊頁面4</h1>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
               {{ form.as_p }}
				{#{{ form.as_table }}#}
                <input type="submit" value="提交" class="btn btn-block btn-info">
            </div>
        </div>
    </div>
</form>

渲染錯誤信息

  • novalidate參數,form標籤中使用,如果添加該參數,不需要校驗或者使用自己的校驗規則
  • 渲染錯誤信息需要傳入error_messages參數在類中

error_messages參數中指定的參數類型

error_messages參數指定錯誤信息類型,以字典的形式指定

  • min_length:不滿足最小長度渲染的信息
  • max_length:超過最大長度渲染的信息
  • required:非空,必填,如果沒填渲染的信息
  • invalid:指定郵箱格式

示例

'''views.py'''
def register(request):
    if request.method == 'GET':
        empty_form = Register()
        return render(request,'register.html',{'form':empty_form})
    else:
        # 實例化,傳入校驗數據
        reg_form_obj = Register(data=request.POST)
        # 判斷校驗是否可以通過
        if reg_form_obj.is_valid():
            # 校驗通過存入數據庫
            print('校驗通過')
            print(reg_form_obj.cleaned_data)
            reg_form_obj.cleaned_data.pop('re_password')
            data = reg_form_obj.cleaned_data
            models.Register.objects.create(**data)
            return HttpResponse('成功')  # 校驗通過返回一個成功
        else:
            # 校驗不通過,返回錯誤信息
            print('校驗不通過')
            print(reg_form_obj.errors)
            return render(request,'register.html',{'form':reg_form_obj})
        

校驗通過和不通過分別返回不同的數據

<!--前端頁面-->
<form action="" method="post" novalidate>
<h1 class="active" style="text-align: center">註冊頁面
  {% for foo in form %}
 <p>{{ foo.label }}:{{ foo }}<span style="color: tomato">{{ foo.errors.0 }}</span></p>
 {% endfor %}
<input type="submit" value="提交">
</form>

需要注意的是,foo.errors返回的是li標籤,是多個,想看單個字段的錯誤信息要指定

image

form渲染樣式之參數配置

上面這樣直接使用渲染的標籤是沒有boostrap組件樣式的,可以通過在類添加參數來定製樣式

導入from django.forms import widgets

  • widget參數指定input框內的文本格式
  • attrs參數指定標籤的樣式
'''Myforms.py'''
class Register(forms.Form):
    username = forms.CharField(max_length=8, min_length=3, label='用戶名',
                               error_messages={'min_length': '太短了吧,敢不敢大於3cm', "required": "該字段不能為空!"}
                               ,widget=widgets.TextInput(attrs={'class':'form-control'}))
    password = forms.CharField(max_length=11, min_length=3, label='密碼',widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    re_password = forms.CharField(max_length=11, min_length=3, label='確認密碼',widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    email = forms.EmailField(label='郵箱', error_messages={'invalid': '格式不正確'},widget=widgets.EmailInput(attrs={'class':'form-control'}))

<form action="" method="post" novalidate>
    <div class="container">
        <h1 class="active" style="text-align: center">註冊頁面3</h1>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                {% for foo in form %}
                    <p>{{ foo.label }}:{{ foo }}<span style="color: tomato">{{ foo.errors.0 }}</span></p>
                {% endfor %}
                <input type="submit" value="提交" class="btn btn-block btn-info">
            </div>
        </div>
    </div>
</form>

image

forms組件全局鉤子和局部勾子

局部鉤子使forms校驗更加精準,比如限制字段長度,是否為數字等···

全局鉤子可以拿到部分字段進行比較,比如確認兩次輸入的密碼是否一致,或者兩次的內容是否一致等···

局部鉤子

from django import forms
from django.core.exceptions import ValidationError
from django.forms import widgets


class Register(forms.Form):
    username = forms.CharField(max_length=8, min_length=3, label='用戶名',
                               error_messages={'min_length': '太短了吧,敢不敢大於3cm', "required": "該字段不能為空!"}
                               , widget=widgets.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(max_length=11, min_length=3, label='密碼',
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(max_length=11, min_length=3, label='確認密碼',
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='郵箱', error_messages={'invalid': '格式不正確'},
                             widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    def clean_username(self):  # 局部鉤子
        # 校驗名字不能以sb開頭
        username = self.cleaned_data.get('username')
        if username.startswith('sb'):

            # 校驗不通過,拋出異常
            raise ValidationError('不能以sb開頭')
        else:
            return username  # 校驗通過,返回username對應的值,這裡不返回username值,後面視圖函數取不到

總結

  • 拋出異常模塊:from django.core.exceptions import ValidationError
  • 局部鉤子需要注意的是,自定義函數後面需要加對應字段的名字,比如clean_username,以及校驗通過後面要返回校驗的字段,不然後面拿不到值

全局鉤子

from django import forms
from django.core.exceptions import ValidationError
from django.forms import widgets


class Register(forms.Form):
    username = forms.CharField(max_length=8, min_length=3, label='用戶名',
                               error_messages={'min_length': '太短了吧,敢不敢大於3cm', "required": "該字段不能為空!"}
                               , widget=widgets.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(max_length=11, min_length=3, label='密碼',
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(max_length=11, min_length=3, label='確認密碼',
                                  widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='郵箱', error_messages={'invalid': '格式不正確'},
                             widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    def clean_username(self):  # 局部鉤子
        # 校驗名字不能以sb開頭
        username = self.cleaned_data.get('username')
        if username.startswith('sb'):

            # 校驗不通過,拋出異常
            raise ValidationError('不能以sb開頭')
        else:
            return username  # 校驗通過,返回username對應的值,這裡不返回username值,後面視圖函數取不到

    def clean(self):  # 全局鉤子
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if password == re_password:
            return self.cleaned_data  # 返回所有校驗通過的數據
        else:
            raise ValidationError('兩次密碼不一致')
from django.shortcuts import render,HttpResponse,redirect
from app01.My_forms import Register
from app01 import models

def register(request):
    if request.method == 'GET':
        empty_form = Register()
        return render(request,'register.html',{'form':empty_form})
    else:
        # 實例化,傳入校驗數據
        reg_form_obj = Register(data=request.POST)
        # 判斷校驗是否可以通過
        if reg_form_obj.is_valid():
            # 校驗通過存入數據庫
            print('校驗通過')
            print(reg_form_obj.cleaned_data)
            reg_form_obj.cleaned_data.pop('re_password')
            data = reg_form_obj.cleaned_data
            models.Register.objects.create(**data)
            return HttpResponse('成功')  # 校驗通過返回一個成功
        else:
            # 校驗不通過,返回錯誤信息
            print('校驗不通過')
            print(reg_form_obj.errors)
            global_error = reg_form_obj.errors.get('__all__')[0]  # 全局鉤子錯誤
            # local_error = reg_form_obj.errors.get('username')[0]  # 局部鉤子錯誤
            return render(request,'register.html',{'form':reg_form_obj,'global_error':global_error})
<form action="" method="post" novalidate>
    <div class="container">
        <h1 class="active" style="text-align: center">註冊頁面3</h1>
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                {% for foo in form %}
                    <p>{{ foo.label }}:{{ foo }}<span style="color: tomato">{{ foo.errors.0 }}</span></p>
                {% endfor %}
                <input type="submit" value="提交" class="btn btn-block btn-info"> <span style="color: #aa100e">{{ global_error }}</span>
            </div>
        </div>
    </div>
</form>

總結

  • 全局鉤子獲取錯誤可以通過__all__獲取
  • 渲染標籤或者頁面要實例化form空對象

錯誤信息顯示

報錯信息顯示順序:

  • 先顯示字段屬性中的錯誤信息,然後再顯示局部鉤子的錯誤信息。
  • 若顯示了字段屬性的錯誤信息,就不會顯示局部鉤子的錯誤信息。
  • 若有全局鉤子,則全局鉤子是等所有的數據都校驗完,才開始進行校驗,並且全局鉤子的錯誤信息一定會顯示

image