zoukankan      html  css  js  c++  java
  • 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())
    

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

  • 相关阅读:
    Android AHandle AMessage
    android java 与C 通过 JNI双向通信
    android 系统给应用的jar
    UE4 unreliable 同步问题
    UE4 difference between servertravel and openlevel(多人游戏的关卡切换)
    UE4 Run On owing Client解析(RPC测试)
    UE4 TSubclassOf VS Native Pointer
    UE4 内容示例网络同步Learn
    UE4 多人FPS VR游戏制作笔记
    UE4 分层材质 Layerd Materials
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/14979257.html
Copyright © 2011-2022 走看看