• 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())
    

    两次密码输入不一样后,在页面上的效果

  • 相关阅读:
    Windows 环境下配置 git bash 的 HOME 默认路径
    SQL Server 2008 r2 输入SQL语句不能自动提示的解决办法
    搭建调用 WebService 的 ASP.NET 网站 (VS2010, C#)
    【问题解决】线程间操作无效:从不是创建控件“textBox1”的线程访问它
    使用 hexdump dump 文件内容
    调用 WebService 浏览器提示 500 (Internal Server Error) 的原因及解决办法
    PHP的文件格式应该以UTF-8无BOM编码
    继电器电路
    ROS零门槛教程系列(二)——Linux常用指令:mkdir、tar、 unzip、cp、scp、mv、rm、find、apt、ssh
    ROS零门槛教程系列(一)——ubuntu安装
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/14979257.html
Copyright © 2020-2023  润新知