表单(二)

● 另一种验证方法---验证器(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': ['请输入正确格式的手机号码']}
    '''

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容