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

python測試開發django-114.ModelForm中區域性鉤子(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屬性中

ModelForm模型表單

區域性鉤子命名規則為clean_欄位名稱,如:clean_city,clean_years。
super() 重寫__init__,可以批量更新class屬性。

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


class SubmitPageForm(forms.ModelForm):

    class Meta:
        model = Submit
        # fields = "__all__"  #全部欄位
        fields = ["city", "years", "details"]
        widgets = {
            "city": widgets.TextInput(attrs={
                "placeholder": "輸入城市:北京/上海/深圳"
            }),
            "years": widgets.TextInput(attrs={
                "placeholder": "輸入年限"}),
            "details": widgets.TextInput(attrs={
                "placeholder": "輸入詳情"}),

        }
        labels = {
            "city": "城 市",
            "years": "年 限",
            "details": "詳 情",
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 批量更新class屬性
        for field in self.fields.values():
            field.widget.attrs.update({'class': 'form-control'})

    def clean_city(self):
        """區域性鉤子判斷城市必須是北京/上海/深圳其中一個"""
        city_val = self.cleaned_data.get('city', '')
        if city_val in ["北京", "上海", "深圳"]:
            return city_val
        else:
            raise forms.ValidationError('城市只能選:北京/上海/深圳')

定義檢視

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


class SubmitView(View):

    def get(self, request):
        form_obj = SubmitPageForm
        return render(request, "submit.html", locals())

    def post(self, request):
        form_obj = SubmitPageForm(request.POST)
        if form_obj.is_valid():
            # data = form_obj.cleaned_data()
            form_obj.save()
            msg = "儲存成功"
            return HttpResponseRedirect('/total')

        else:
            # 全域性鉤子自定義錯誤提示獲取
            # print(form_obj.errors.get('__all__'))
            # error_msg = form_obj.errors.get('__all__')
            return render(request, "submit.html", locals())

模板內容

模板內容如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>提交頁面</title>
    <meta charset="utf-8">
	<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
	<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
	<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">

   <form role="form" action="" method="POST" id="detail-form" >
    {% csrf_token %}
    {% for field in form_obj %}
        <div class="form-group">
            {{ field.label_tag }}
            {{ field }}
            <div style="color: red"> {{ field.errors }} </div>

        </div>

    {% endfor %}
    <p>
        <input type="submit" value="提交" >
    </p>
</form>
</div>
</body>
</html>

頁面效果

輸入不合法的內容,會顯示field.errors內容

全域性鉤子

針對單個欄位校驗可以用區域性鉤子實現,如果我們要校驗多個欄位,比如校驗註冊的時候輸入2次密碼一致,可以用全域性鉤子實現。
定義全域性鉤子使用clean方法

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


class SubmitPageForm(forms.ModelForm):

    class Meta:
        model = Salary
        fields = "__all__"  #全部欄位
    # 省略中間程式碼。。。。。

    # 全域性鉤子
    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

前端可以通過fomr_obj.errors.__all__ 獲取到內建校驗器的全部錯誤資訊