表單(二)

● 另一種驗證方法---驗證器(validator),它的效果和'error_messages'參數類似

以下是一些常用的驗證器:

<1> MaxValueValidator:驗證最大值。

<2> MinValueValidator:驗證最小值。

<3> MinLengthValidator:驗證最小長度。

<4> MaxLengthValidator:驗證最大長度。

<5> EmailValidator:驗證是否是郵箱格式。

<6> URLValidator:驗證是否是URL格式。

<7> RegexValidator:如果還需要更加復雜的驗證,那么我們可以通過正則表達式的驗證器:RegexValidator。比如現在要驗證手機號碼是否合格,那么我們可以通過以下代碼實現:

from django import forms
from django.core import validators

class MessageBoardForm(forms.Form):
    
    telephone=forms.CharField(validators=[validators.RegexValidator(r'1[345678]\d{9}',
    message='請輸入正確格式的手機號碼!')])

驗證器的基礎示例用法:

# forms

from django import forms
from django.core import validators # 導入驗證器

class MessageBoardForm(forms.Form):
    
    #email=forms.EmailField(label='郵箱',error_messages=dict(required='必須輸入email字段',
    #invalid='請輸入正確的郵箱地址格式'))
    
    # 通過validators參數指定驗證器(list類型,傳遞message,自定義驗證信息)
    email=forms.EmailField(validators=[validators.EmailValidator(message='請輸入正確的郵箱地址格式')])
    
# views

from django.shortcuts import render
from django.views.generic import View
from .forms import MessageBoardForm
from django.http import HttpResponse
from django.forms.utils import ErrorDict

class FormView(View):

    def get(self,request):
        return render(request,'index.html')

    def post(self,request):
        form=MessageBoardForm(request.POST)
        if form.is_valid():
            return HttpResponse('數據提交成功!!!')
        else:
            print(form.errors.get_json_data()) # 依舊調用get_json_data()打印錯誤信息
            return HttpResponse('數據提交失敗!')
            
# index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Message Board</title>
</head>
<body>
    <form action="" method="post">
        <label> 郵箱:
            <input type="text" name="email"> # 單純弄一個文本框測試效果
        </label>
        <input type="submit" value="submit">
    </form>
</body>
</html>

訪問url,隨便輸入,終端信息:

{'email': [{'message': '請輸入正確的郵箱格式', 'code': 'invalid'}]}

換成之前的 error_messages 示例,對比一下效果(基本差不多...):

from django import forms
from django.core import validators

class MessageBoardForm(forms.Form):
    
    email=forms.EmailField(label='郵箱',error_messages=dict(required='必須輸入email字段',
    invalid='請輸入正確的郵箱地址格式'))
    
    # email=forms.EmailField(validators=[validators.EmailValidator(
    #     message='請輸入正確的郵箱地址格式'
    # )])

    '''
    {'email': [{'code': 'invalid', 'message': '請輸入正確的郵箱地址格式'}]}
    '''

● 自定義驗證方法---對字段進行驗證(單單有驗證器是滿足不了需求的)

實現'注冊'需求,比如手機號碼'telephone'字段值如果已經存在,那就提示'注冊失敗',終端打印'出錯信息'
顯然,這個需求得有數據庫的支持了...

步驟1: 先定義模型(與前端提交的數據進行比對!)

# front.models

from django.db import models

class User(models.Model):
    # 簡單定義兩個字段
    username=models.CharField(max_length=100)
    telephone=models.CharField(max_length=11)
    # 下面這句其實已經滿足需求了,為了演示,就不這么寫
    #telephone=models.CharField(max_length=11,unique=True)

步驟2: 定義'表單'(代碼與'model'類似)

# front.forms
class RegisterForm(forms.Form):
    username=forms.CharField(max_length=100)
    telephone=forms.CharField(validators=[validators.RegexValidator(
        r'1[345678]\d{9}',message='請輸入正確格式的手機號碼'
    )])

步驟3: 后端'views',前端模板編寫:

# front.views

class RegisterView(View):
    
    # get加載空表單
    def get(self,request):
        return render(request,'register.html')

    def post(self,request):
        form=RegisterForm(request.POST)
        if form.is_valid():
            username=form.cleaned_data.get('username')
            telephone=form.cleaned_data.get('telephone')
            # 把獲取的字段,傳給模型,插入數據庫
            # 這里暫未涉及對'字段'的判斷
            User.objects.create(username=username,telephone=telephone)
            return HttpResponse('注冊成功!')
        else:
            print(form.errors.get_json_data())
            return HttpResponse('注冊失敗')
    
# register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="" method="post"> # 簡單定義一個表單
        <table>
            <tr>
                <td>用戶名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>手機號碼:</td>
                <td><input type="text" name="telephone"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="提交"></td>
            </tr>
        </table>
    </form>
</body>
</html>

上述步驟完成后,刷新網頁看看效果,基本的'注冊'功能已經有了'雛形',下來,我們對'telephone'字段'自定義驗證':

# front.forms
class RegisterForm(forms.Form):
    username=forms.CharField(max_length=100)
    telephone=forms.CharField(validators=[validators.RegexValidator(
        r'1[345678]\d{9}',message='請輸入正確格式的手機號碼'
    )])

    def clean_telephone(self): # clean_field()
        # 獲取telephone字段并與數據庫對比
        telephone=self.cleaned_data.get('telephone')
        exists=User.objects.filter(telephone=telephone).exists()
        if exists:
            raise forms.ValidationError('{} 已經存在,請變更號碼'.format(telephone))
        # clean_field()方法一定要返回field...
        return telephone

刷新網頁,插入相同的'telephone'看看效果

● 批量自定義字段驗證---clean()方法的使用
現在要在上述示例的基礎上,添加'password'字段的驗證,如果兩次'password'輸入不一致,也提示'注冊失敗',并且的'終端'打印出'驗證失敗信息'

class RegisterForm(forms.Form):
    username=forms.CharField(max_length=100)
    telephone=forms.CharField(validators=[validators.RegexValidator(
        r'1[345678]\d{9}',message='請輸入正確格式的手機號碼'
    )])
    pw1=forms.CharField(max_length=16,min_length=6) # 新增'密碼'字段
    pw2=forms.CharField(max_length=16,min_length=6)

    def clean_telephone(self):
        ......
        return telephone
    
    # 如果來到了clean()方法,說明之前的每一個字段都驗證成功了!
    def clean(self):
        # 查看源碼clean()方法返回的是 self.cleaned_data
        clean_data=super().clean() # 調用父類的clean()方法,返回clean_data
        pw1=clean_data.get('pw1')
        pw2=clean_data.get('pw2')
        if pw1 != pw2:
            raise forms.ValidationError(message='兩次密碼輸入不一致!')
        return clean_data # clean()返回的一定是 clean_data
    '''
    # 這里這么寫也可以
    def clean(self):
        clean_data=super().clean()
        pw1=self.cleaned_data.get('pw1') # 不使用clean_data,而使用self.cleaned_data,一樣的效果
        pw2=self.cleaned_data.get('pw2')
        if pw1!=pw2:
            raise forms.ValidationError('兩次密碼輸入不一致,請重新輸入')
        return clean_data
    '''

前端模板修改一下:

# register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="" method="post">
        <table>
            <tr>
                <td>用戶名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>手機號碼:</td>
                <td><input type="text" name="telephone"></td>
            </tr>
            <tr>
                <td>密碼:</td> # 新增passwore文本框,注意type='password'類型
                <td><input type="password" name="pw1"></td>
            </tr>
            <tr>
                <td>重復密碼:</td>
                <td><input type="password" name="pw2"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="提交"></td>
            </tr>
        </table>
    </form>
</body>
</html>

刷新網頁,看看'密碼不一致'效果

? 需求變更為'用戶名'或'手機號碼'存在,就提示注冊失敗,并在終端輸出'錯誤信息',我們使用clean()方法[處理多個字段]一下子搞定[不再定義'clean_field()'對'單獨字段'進行處理]

from django import forms
from django.core import validators●
from .models import Register
from django.db.models import Q

class RegiterForm(forms.Form):

    username=forms.CharField(max_length=20)
    telephone=forms.CharField(validators=[validators.RegexValidator(r'1[345678]\d{9}',
    message='請輸入正確格式的手機號碼!')])
    password1=forms.CharField(max_length=16,min_length=6)
    password2=forms.CharField(max_length=16,min_length=6)

    '''def clean_telephone(self):
        telephone=self.cleaned_data.get('telephone')
        exists=Register.objects.filter(telephone=telephone).exists()
        if exists:
            raise forms.ValidationError(message='手機號碼已經存在,請變更手機號碼!')
        return telephone

    def clean_username(self):
        username=self.cleaned_data.get('username')
        exists=Register.objects.filter(username=username).exists()
        if exists:
            raise forms.ValidationError(message='用戶名已經存在,請變更注冊名')
        return username'''

    def clean(self):

        clean_data=super().clean()
        password1=clean_data.get('password1')
        password2=clean_data.get('password2')
        username=clean_data.get('username')
        telephone=clean_data.get('telephone')

        if password1 != password2:
            raise forms.ValidationError(message='密碼輸入不一致,請重新輸入!')

        exists = Register.objects.filter(Q(telephone=telephone) | Q(username=username)).exists()
        if exists:
            raise forms.ValidationError(message='用戶名或手機號碼已經存在,請變更用戶名或手機號碼!')
        
        return clean_data

什么時候使用clean_field(),什么時候使用clean(),看情況而定了...

● 處理'錯誤信息',使其顯示更加友好,之前的錯誤信息是這樣的:

dict1={'telephone': [{'code': 'invalid', 'message': '請輸入正確格式的手機號碼'}], '__all__': [{'code': '', 'message': '兩次密碼輸入不一致!'}]}

我們改進一下,變成下面這樣的,顯然更為'友好':

{'telephone': ['請輸入正確格式的手機號碼'], '__all__': ['兩次密碼輸入不一致!']}

示例:

# fomrs
from django import forms
from django.core import validators
from .models import User

class RegisterForm(forms.Form):
    username=forms.CharField(max_length=100)
    ......

    def clean_telephone(self):
        ......
        return telephone

    def clean(self):
        ......
        return clean_data

    def get_errors(self):# 自定義get_errors()方法,改進'錯誤信息'
    
        errors=self.errors.get_json_data() # 調用 get_json_data()方法,獲取'信息dict'
        new_errors={} # 定義空dict,把改進后的結果,扔進來
        for key,message_dicts in errors.items():
            messages=[] # 收集字典的'value'值
            for message_dict in message_dicts:
                message=message_dict['message']
                messages.append(message)
            new_errors[key]=messages
        return new_errors

# views

class RegisterView(View):

    def get(self,request):
        return render(request,'register.html')

    def post(self,request):
        ......
        else:
            print(form.get_errors()) # 不再是調用get_json_data()
            return HttpResponse('注冊失敗')
    '''
    {'__all__': ['兩次密碼輸入不一致!'], 'telephone': ['請輸入正確格式的手機號碼']}
    '''

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容