zoukankan      html  css  js  c++  java
  • Django框架之八 form组件 钩子函数

    Django框架之八  form组件  钩子函数

    一、什么是form组件,可以干什么

    1.forms组件就是一个类,可以检测前端传来的数据,是否合法。
    例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等  >>>校验数据
    2.还可以前端页面搭建   >>>  渲染页面
    3.展示错误信息   >>>   渲染错误信息
    

      

    二、form组件的使用

    1.使用语法

    from django.shortcuts import render, HttpResponse
    from django import forms
    
    #1.先写一个类,继承Form
    class MyForm(forms.Form):
        #定义属性,用来校验
        #限制最大长度为8,最小长度为3
        name = forms.CharField(max_length=8,min_length=3)
        pwd = forms.CharField(max_length=8,min_length=3)
        #校验邮箱
        email = forms.EmailField()
    
    
    #2.在视图函数中使用MyForm来校验数据
    #实例化产生对象,将需要校验的数据传入(可以传字典,可以不传),这个数据是前端传递过来的数据
    myform = MyForm(request.POST)
    
    #3.校验,is_valid如果是true表示校验成功(满足myform里面的所有条件),反之,验证失败
    if myform.is_valid():
        print(myform.cleaned_data)  #打印校验通过的数据
        return HttpResponse('校验成功')
        
    else:
        #校验失败的信息  是一个字典,它是所有错误信息 {错误字段名:[错误原因]}
        print(myform.errors)  #errors是一个列表,表示每个字段的错误信息
        return HttpResponse('校验失败')
    

      方法总结:

    clean_data  验证通过的数据
    errors  错误数据的对象
    errors.as_data  错误数据的信息
    

     注意事项 

    1.自定义的类中所有的字段默认都是必须要传值的
    2.可以额外传入类中没有定义的字段名,forms组件不会去校验,也就是多传没有关系
    3.实例化时,传入必须是字典,也可以不传
    

      

    form_obj = views.LoginForm({'username':'jason','password':'123456','email':'123@qq.com'})
    form_obj.is_valid()
    True
                        
    form_obj = views.LoginForm({'username':'jason','password':'123456'})
    form_obj.is_valid()
    False
                        
    form_obj = views.LoginForm({'username':'jason','password':'123456','email':'123@qq.com','hobby':'read'})
    form_obj.is_valid()
    True
    

      2.组件的参数及其他操作方式

    max_length   #代表该字段最长可以为多少
    min_length    #代表该字段最短可以为多少
    error_messages    #设置错误信息的属性
    required      #默认值为True,意思是你前端传来的字段必须有它,没有的话校验失败
    label    #注释信息label ='用户名'   在前端渲染可以直接对象.label获取值
    initial   #设置默认值
    
    widget  #控制标签属性和样式
    widget = widgets.PasswordInput()   #你在模板渲染的时候,就会渲染成Input框,password样式
    控制标签属性
    widget = widgets.PasswordInput(attrs={'class':'form-control c1 c2','username':'jason'})
    
    
    #例子
    pwd = forms.CharField(max_length=8,min_length=3,required=True,label='密码',
              error_messages = {'max_length':'最长是8','min_length':'最短是3','required':'这个必须填'}
                )        
    

      

    其他操作方式
    # 单选的radio框
    gender = forms.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )
    # 单选select
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )
    # 多选的select框
    hobby1 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )
    # 单选的checkbox
    keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )
    # 多选的checkbox
    hobby2 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )    
    

    三、渲染页面、渲染错误信息  

    #form组件可以在视图函数中使用,也可以在模板中使用
    #视图层
    def reg(request):
        myform = MyForm()
       if request.method == 'POST':
           myform = MyForm(request.POST)
         print(request.POST)  #<QueryDict: {'name': ['小张'], 'pwd': ['123123'], 'email': ['119@qq.com']}>
    
        return render(request,'reg.html',locals())  #把myform对象传到前端页面了
    

      

    #模板层
    #第一种渲染方式(封装程度太高,一般只用于本地测试,通常不适用)
    <form action='' method='post'>
             {{ myform.as_p }}
             {{ myform.as_ul }}
             {{ myform.as_table}}
            <input type='submit' value = '提交'></input>
     </form>
    
    #第二种渲染页面的方式(可扩展性高,书写麻烦)
    <form action='' method='post'>
            {{myform.name.label}}:{{myform:name}} 
            {{myform.pwd.label}}:{{myform:pwd}} 
            {{myform.email.label}}:{{myform:email}} 
            <input type='submit' value = '提交'></input>
     </form>
    
    #第三种渲染方式(推荐)
    <form action='' method='post'>
            {% for foo in myform %}
               <p> {{ foo.lable }} : {{ foo }} 
               <span>{{ foo.errors.0 }}</span>   //注意后面加0  错误信息
               </p>
            <input type='submit' value = '提交'></input>
    </form>      
    复制代码
    

      

     注意事项:

    1.forms组件在帮你渲染页面的时候 只会渲染获取用户输入的标签  提交按钮需要你手动添加               
    2.input框的label注释  不指定的情况下 默认用的类中字段的首字母大写
    

    四、钩子函数(HOOK)

    forms组件暴露给用户,可以自定义的校验规则    用法:在自定义的form类中定义一个函数即可

    1.局部钩子(针对某一个字段做额外的校验) 

    名字叫:clean_字段名,内部,取出该字段进行校验,如果通过,将该字段返回,如果失败,抛异常

      

    函数名:clean_字段名字
    def clean_name(self):   #self是当前form对象
        name = self.cleaned_data.get('name')    #从验证通过的数据中再次筛选
        if '666' in username:
            #失败,抛异常,把异常信息以('key','value')形式写入add_error中
            self.add_error('name','不能包含666')   
        #正常,把name返回到clean_data,将name写入clean_data字典中
        return name
    

    注意点:

    • 这些函数是写在自定义类form里面的,需要校验哪个字段就以 clean_字段名写一个校验函数
    • 校验失败,通过add_error('字段名','错误信息'),以{key:value}形式写入到errors字典中
    • 校验成功,返回name到clean_data,写入clean_data字典中

      

    2.全局钩子(针对多个字段做额外的校验)

    #重写clean方法
    def clean(self):
        #能走到这步,说明前面的校验已经通过了,下面校验两次两次密码是否输入一致
        pwd = self.clean_data.get('pwd')
        re_pwd = self.clean_data.get('re_pwd')
        if pwd == re_pwd:
            #校验成功,直接返回clean_data
            return self.clean_data
        else:
            #校验失败,把错误信息放入errors中
            self.add_error('re_pwd','两次密码输入不一致')
            或者
            #抛出异常
            raise ValidationError('两次密码不一致')
    

    注意点:

    • 校验失败,有两种写法,抛异常,将异常信息以{'__all__':[value,]}写入errors字典中
    • 校验成功,返回clean_data字典
    • 抛出异常类型为 ValidationError, from django.core.exceptions import ValidationError导入

    钩子错误信息渲染注意点:

    • 局部钩子抛出的异常会添加到该字段中的错误信息中,获取错误信息

       前端: for循环对象  {{ foo.errors.0}}

    • 全局钩子抛出的异常会添加到__all__中,获取错误信息:

      后端: myform.errors.get('__all__')[0]   注意先判断myform.errors.get('__all__')是否存在

         前端:{{ myform.errors.__all__.0}}

    • 能走到这些函数,说明前面的form组件的那些校验都成功了,此时就可以从clean_data中取数据,因为此时clean_data中的数据全部符合,都找的到,而且clean_data是字典比较好取值。

     五、完整的form组件校验

     1.视图层

      

    from django.shortcuts import render, HttpResponse, redirect
    
    # forms组件数据校验的功能
    # 第一步:先要继承Form
    from django import forms
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    
    
    # 写一个类
    class MyForm(forms.Form):
        # 定义一个属性,可以用来校验字符串类型
        # 限制最大长度是8,最小长度是3
        name = forms.CharField(max_length=8, min_length=3, label='用户名'
                               error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
                               widget=widgets.TextInput(attrs={'class': 'form-control'}))
        
        pwd = forms.CharField(max_length=8, min_length=3, required=True, label='密码',
                              error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
                              widget=widgets.PasswordInput())
        
        re_pwd = forms.CharField(max_length=8, min_length=3, required=True, label='确认密码',
                                 error_messages={'max_length': '最长是8', 'min_length': '最短是3', 'required': '这个必须填'},
                                 widget=widgets.PasswordInput())
        # 校验是否是邮箱格式
        email = forms.EmailField(label='邮箱', error_messages={'required': '这个必须填', 'invalid': '不符合邮箱格式'})
    
        # aa = forms.CharField(label='选择', error_messages={'required': '这个必须填', 'invalid': '不符合邮箱格式'},widget=widgets.CheckboxInput())
        def clean_name(self):
            # self:当前form对象
            name = self.cleaned_data.get('name')
            if name.startswith('sb'):
                # 失败,抛异常
                raise ValidationError('不能以傻逼开头')
            # 正常,把name返回
            return name
    
        def clean(self):
            pwd = self.cleaned_data.get('pwd')
            re_pwd = self.cleaned_data.get('re_pwd')
            if pwd == re_pwd:
                return self.cleaned_data
            else:
                raise ValidationError('两次密码不一致')
    
    
    def index_form(request):
        # 生成对象时(实例化),需要传入要校验的数据(字典)
        if request.method == 'GET':
            myform = MyForm()
            return render(request,'indxe2.html',locals())
        elif request.method == 'POST':
            myform = MyForm(request.POST)
            if myform.is_valid():
                # print(myform.cleaned_data)   # 验证通过的数据
                # models.User.objects.create(name='lqz',pwd='123',re_pwd='123)
                myform.cleaned_data.pop('re_pwd')
                models.User.objects.create(**myform.cleaned_data)
                return redirect('http://www.baidu.com')
            else:
                all_error = myform.errors.get('__all__')
                if all_error:
                    all_error = all_error[0]
                 # print(myform.errors.as_data)
    
        return render(request, 'indxe3.html', locals())
    复制代码
    

     2.模板层

    <form action="" method="post" novalidate>
        {% for foo in myform %}
            <p>{{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span></p>
        {% endfor %}
    
        <input type="submit" value="提交"><span>{{ all_error }}</span>
    </form>
    

      

     

  • 相关阅读:
    npm --save-dev 与 --save 的区别
    Vue 简单实例 购物车2
    Vue 简单实例 购物车1
    node.js富文本编辑器
    使用jquery操作session
    浏览器窗口之间传递数据
    批量修改文件编码格式
    具有动态效果的响应式设计
    Ajax请求全局配置
    html实体转换
  • 原文地址:https://www.cnblogs.com/ltyc/p/13875333.html
Copyright © 2011-2022 走看看