1. 程式人生 > 其它 >python測試開發django-106.form表單中區域性鉤子(clean_)和全域性鉤子校驗

python測試開發django-106.form表單中區域性鉤子(clean_)和全域性鉤子校驗

前言

在實際開發中,不僅僅是對輸入框字元的格式校驗,比如註冊功能,註冊賬號還得校驗資料庫是否已經有賬號被註冊過了。
有些場景不僅僅是對單個輸入框的字元校驗,比如修改密碼的時候,會涉及2個輸入框的資料格式校驗,像這些複雜的場景校驗需用到校驗鉤子來實現。
校驗form表單資料合法性,is_valid()方法呼叫順序:

  • 1.欄位規則校驗,字元長度,是否必填等基本校驗
  • 2.validators校驗(RegexValidator校驗器或自定義校驗函式)
  • 3.區域性鉤子(類中定義的以clean_欄位名命名的函式,校驗正常必須返回該欄位的值self.cleaned_data.get('name'))
  • 4.全域性鉤子(類中定義的函式名clean,校驗正常必須返回該物件的校驗結果值return self.cleaned_data)
  • 5.每一步通過校驗單結果都以字典形式儲存在類物件的cleaned_data屬性中

註冊示例

登錄檔單RegisterForm

from django import forms
from django.core.exceptions import ValidationError
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/


class RegisterForm(forms.Form):
    """登錄檔單"""
    username = forms.CharField(label="使用者名稱",
                               required=True,
                               min_length=3,
                               max_length=20,
                               error_messages={'required': '不能為空',
                                             })
    password = forms.CharField(max_length=16,
                               min_length=6,
                               required=True,
                               label="密碼",
                               widget=forms.PasswordInput,
                               error_messages={
                                   'required': '密碼不能為空',
                                   'min_length': '密碼不能少於6位字元',
                                   'max_length': '密碼不能大於16位字元',
                               })
    email = forms.EmailField(required=False,
                             error_messages={'invalid': '郵箱引數不合法'})

註冊檢視

from django.shortcuts import render
from django.contrib.auth.models import User
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/


def registerView(request):
    """註冊檢視"""
    if request.method == "GET":
        form_obj = RegisterForm()
        return render(request, "register_form.html", locals())
    if request.method == "POST":
        form_obj = RegisterForm(request.POST)
        if form_obj.is_valid():
            username = form_obj.cleaned_data.get("username")
            password = form_obj.cleaned_data.get("password")
            email = form_obj.cleaned_data.get("email")
            try:
                user = User.objects.create_user(username=username,
                                                password=password,
                                                email=email)
                user.save()
                error_msg = "註冊成功!"
            except Exception as msg:
                error_msg = "註冊賬號異常"
                print("註冊賬號異常:", msg)
        return render(request, "register_form.html", locals())

註冊模板

<form action="" method="POST" id="login-form" style="text-align:center;">
    {% csrf_token %}
    {% for field in form_obj %}
        <p>
            {{ field.label_tag }}
            {{ field }}
            {{ field.errors }}
        </p>
    {% endfor %}
    <p>
        {{ error_msg }}
    </p>
    <p>
        <input type="submit" value="立即註冊" >
    </p>
</form>

當註冊一個已存在的賬號test,會報註冊賬號異常: (1062, "Duplicate entry 'test' for key 'username'")
因為資料庫已經存在賬號test, 寫入資料庫時會報這個錯:Duplicate entry

區域性鉤子

在Form類裡面定義區域性鉤子,校驗資料庫,格式 clean_校驗欄位(self):

from django import forms
from django.core.exceptions import ValidationError
# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class RegisterForm(forms.Form):
    """登錄檔單"""
    username = forms.CharField(label="使用者名稱",
                               required=True,
                               min_length=3,
                               max_length=20,
                               error_messages={'required': '不能為空',
                                             })
    password = forms.CharField(max_length=16,
                               min_length=6,
                               required=True,
                               label="密碼",
                               widget=forms.PasswordInput,
                               error_messages={
                                   'required': '密碼不能為空',
                                   'min_length': '密碼不能少於6位字元',
                                   'max_length': '密碼不能大於16位字元',
                               })
    email = forms.EmailField(required=False,
                             error_messages={'invalid': '郵箱引數不合法'})

    # 區域性鉤子
    def clean_username(self):
        """判斷資料庫是否已存在"""
        val = self.cleaned_data.get('username')  # 獲取username欄位值
        user = User.objects.filter(username=val)      # 在資料庫中判斷是否存在使用者名稱
        if not user:
            return val  # 如果通過校驗,那麼把值直接原封不動返回即可
        else:
            raise ValidationError('使用者已經註冊')

重複註冊的時候,就會提示使用者已被註冊

全域性鉤子

在forms.py裡面的BaseForm類可以看到clean方法,此方法是在每個Field欄位校驗之後觸發,校驗失敗錯誤資訊儲存到 errors {'all':[e,]}。
校驗成功返回self.cleaned_data

def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() has been
        called on every field. Any ValidationError raised by this method will
        not be associated with a particular field; it will have a special-case
        association with the field named '__all__'.
        """
        return self.cleaned_data

於是可以重寫clean()方法,校驗輸入的2次密碼是不是一致

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/

class RegisterForm(forms.Form):
    """登錄檔單"""
    ......

    # 全域性鉤子
    def clean(self):
        """在通過基礎驗證的乾淨資料中get獲取欄位"""
        pwd1 = self.cleaned_data.get('password')
        pwd2 = self.cleaned_data.get('password2')
        if pwd1 and pwd2:  # 這裡判斷2個欄位都是經驗通過
            if pwd1 == pwd2:
                # 資料沒問題,那麼原封不動返回即可
                return self.cleaned_data
            else:
                # 錯誤資訊儲存到 errors {'__all__':[e,]}
                raise ValidationError('兩次密碼輸入不同')
        else:
            return self.cleaned_data

在檢視函式中可以拿到全域性鉤子錯誤資訊:form_obj.errors.get('all')[0]

# 作者-上海悠悠 QQ交流群:717225969
# blog地址 https://www.cnblogs.com/yoyoketang/


def registerView(request):
    """註冊檢視"""
    if request.method == "GET":
        form_obj = RegisterForm()
        return render(request, "register_form.html", locals())
    if request.method == "POST":
        form_obj = RegisterForm(request.POST)
        if form_obj.is_valid():
            username = form_obj.cleaned_data.get("username")
            password = form_obj.cleaned_data.get("password")
            email = form_obj.cleaned_data.get("email")
            try:
                user = User.objects.create_user(username=username,
                                                password=password,
                                                email=email)
                user.save()
                error_msg = "註冊成功!"
            except Exception as msg:
                error_msg = "註冊賬號異常"
                print("註冊賬號異常:", msg)
        else:
            # 全域性鉤子自定義錯誤提示獲取
            print(form_obj.errors.get('__all__')[0])
            error_msg = form_obj.errors.get('__all__')[0]

        return render(request, "register_form.html", locals())

兩次密碼輸入不一樣後,在頁面上的效果