#form表单的校验、局部钩子及全局钩子#

## views.py 视图函数 ##

from django import forms                #调用forms模块
    from django.forms import widgets        #调用widgets模块,用来对form组件的参数配置。
    from django.core.exceptions import ValidationError  #调用 ValidationError 模块。用来手动触发raise错误。
    from fileupdate.models import *      #载入必要的数据库列表。


    class FormReg(forms.Form):

        name = forms.CharField(min_length=4, widget=widgets.TextInput(attrs={'class': 'form-control '}), label='姓名',
                               error_messages={'required': '*不能为空', })
        pwd = forms.CharField(min_length=4, widget=widgets.PasswordInput(attrs={'class': 'form-control'}), label='密码')
        r_pwd = forms.CharField(min_length=4, widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                                label='确认密码')
        email = forms.EmailField(widget=widgets.EmailInput(attrs={'class': 'form-control'}), label='邮箱')
        tel = forms.CharField(max_length=13, label='电话', widget=widgets.TextInput(attrs={'class': 'form-control'}))
        ##字段的校验,通过对widget的属性设置,可定义INPUT标签的type类型,以及标签的其他属性。通过对label设置,可以自定义form渲染时的标签名,
        ##另外,通过对error_messages属性设置,可对验证信息进行自定义。注意:字典中错误信息的key值是固定的
    
        def clean_name(self):    #局部钩子   注意:名字必须为clean_%s ,这是根据源码来设置的。
                                #其原理是,当字段校验完毕后,再进行查找是否有以clean_开头的函数名,如果有,就调用该函数,
                                #运行我们自定义的函数,如果满足条件就返回当前被校验字段的内容。否则手动触发ValidationError错误,源码中会捕获并将值返回。
            val = self.cleaned_data.get('name')     #通过cleaned_data获得对应字段的'干净数据'
            user_obj = User.objects.filter(name=val).first()  #与对应的数据库中字段相比较,并获得一个字段对象
            if not user_obj:   #对字段进行判断,如果为空(数据库中没有对应的名字),那么返回这个校验值。
                return val
            else:
                raise ValidationError('名字存在')   #如果存在,那么手动触发异常(异常名为ValidationError),并设置自定义内容。
    
        def clean(self):   #全局钩子  注意:名字必须为clean,这是根据源码来设置的。
                         #其原理是对校验完毕的字段,再进行字段间的校验。当字段校验完毕,查找是否有clean的函数,如果有就运行该
                         #函数,其功能是对所有校验的字段进行校验比对。如果满足条件,就将cleaned_data返回(这与源码相匹配)
                         #如果不满足就手动触发ValidationError错误。
            pwd = self.cleaned_data.get('pwd') 
            r_pwd = self.cleaned_data.get('r_pwd')
            if pwd and r_pwd:   #如果两个字段中一个为空值那么就不用再进行校验。直接返回cleaned_data,通过校验功能返回错误信息。
                if pwd == r_pwd:
                    return self.cleaned_data
                else:
                    raise ValidationError('两次密码不一致!')
            else:
                return self.cleaned_data

    def reg(request):
    
        if request.method == 'POST':  #如果是一次POST提交,那么进行校验。
            formreg = FormReg(request.POST)  #对提交的信息实例化。
    
            if formreg.is_valid():  #通过is_valid()方法进行判断,(注意:当执行这个函数时,将对所有字段进行校验,运行局部钩子和全局钩子)
                return HttpResponse('OK')
            else:
                print('cleaneddata', formreg.cleaned_data)
                print('errordata', formreg.errors)
                error = formreg.errors.get('__all__') #当设置了全局钩子时,要设置一个变量来获得全局钩子返回的错误信息。
                            #这是由于,全局钩子的错误在form对象的errors中,当clean()方法抛出异常时,源码会自动捕获,并将错误
                            #存储在errors字典中,其中键名'__all__'就是全局钩子的变量。
                return render(request, 'reg.html', locals())
    
        formreg = FormReg()   #当为get请求时,实例化一个空的对象,通过这个空的实例化对象可以渲染前段,自动生成form表单。
        return render(request,'reg.html', locals())

 

## reg.html 前端页面 ##

  <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>reg</title>
        <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
              integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    </head>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form action="" method="post" novalidate>
                    {% csrf_token %}
                    {% for field in formreg %}
                        <p>
                            <label for="">{{ field.label }}</label>
                        {{ field }} 
                            {% if field.name == 'r_pwd' %}
                                <span style="color: crimson">{{ field.errors.0 }} {{ error.0 }}</span>
                            {% else %}
                                <span style="color: crimson">{{ field.errors.0 }}</span>
                            {% endif %}
    
                        </p>
                    {% endfor %}
    
                    <p><input type="submit"></p>
                </form>
            </div>
        </div>
    </div>
    
    </body>
    </html>

View Code