• Django高级之forms组件


    forms组件之校验字段

    # 第一步:定义一个类,继承forms.Form
    # 第二步:在类中写字段,要校验的字段,字段属性就是校验规则
    # 第三步:实例化得到一个Form对象,把要校验的数据传入
    # 第四步:调用register_form.is_valid()校验,校验通过就是True
    # 第五步:校验通过有register_form.cleaned_data
    # 第六步:校验不通过 register_form.errors
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册用户</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <div class="col-md-6 col-md-offset-3">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title">注册功能</h3>
            </div>
            <div class="panel-body">
                <h1 class="text-center">注册</h1>
                <form action="" method="post">
                    <p>用户名:<input type="text" name="name" class="form-control"></p>
                    <p>密码:<input type="password" name="password" class="form-control"></p>
                    <p>确认密码:<input type="password" name="re_password" class="form-control"></p>
                    <p>邮箱:<input type="text" name="email" class="form-control"></p>
                    <input id="id_btn" type="submit" class="btn btn-primary btn-block">
            </div>
        </div>
    </div>
    </body>
    </html>
    校验手动渲染示例html
    class User(models.Model):
        name = models.CharField(max_length=32,verbose_name='用户名')
        password = models.CharField(max_length=32,verbose_name='密码')
        email = models.EmailField(verbose_name='邮箱')
    创建用户表models.py
    # views.py
    
    from django.shortcuts import render
    from django import forms
    
    #定义类
    class RegisterForm(forms.Form):
        # name字符串类型最大8位,最小3位
        name = forms.CharField(max_length=8, min_length=3, label='用户名')
        # password字符串类型最大8位,最小3位
        password = forms.CharField(max_length=8, min_length=3, label='密码')
        # re_password字符串类型最大8位,最小3位
        re_password = forms.CharField(max_length=8, min_length=3, label='确认密码')
        # email必须符合邮箱格式,xxx@xx.com
        email = forms.EmailField(label='邮箱')
       
    
    #在视图中使用
    register_form = RegisterForm(request.POST)
    if register_form.is_valid():
          # 校验通过,存
          # 取出校验通过的数据
          print('校验通过')
          print(register_form.cleaned_data)
        
            # 存储前先删除多余的字段
            register_form.cleaned_data.pop('re_password')
           # 将数据存入数据库的user表中
           models.User.objects.create(**register_form.cleaned_data)
            
    else:
         # 校验不通过
         print('校验不通过')
         print(register_form.errors)

    forms组件之渲染标签

    def register(request):
        if request.method == 'GET':
            # GET请求没有数据,需要生成一个空form对象
            # 这个form跟下面没有关系,是get请求过来的得到一个空form
            register_form = RegisterFrom()
            # 传到前端页面后,通过form进行渲染
            return render(request, 'register.html', {'form': register_form})
        else:
            register_form = RegisterFrom(request.POST)
    
            if register_form.is_valid():
                print('效验通过')
                print(register_form.cleaned_data)
                register_form.cleaned_data.pop('re_password')
                models.User.objects.create(**register_form.cleaned_data)
            else:
                print('效验不通过')
                print(register_form.errors)
    
            return render(request,'register.html')
    视图层:views.py

    渲染方式一

    可扩展性强,但是需要书写的代码太多,一般情况下不用

     <h2>通过form自动渲染一</h2>
    <form action="" method="post">
         <p>用户名 {{ form.name }}</p>   
         <p>密码 {{ form.password }}</p>    
        <p>确认密码 {{ form.re_password }}</p>    
        <p>邮箱 {{ form.email }}</p>    
    <input type="submit" value="提交"></form>

    渲染方式二

    推荐使用,代码书写简单,并且可扩展性强

    <h2>通过form自动渲染二(基本用这种)</h2>
    
    <form action="" method="post">
        {% for item in form %}
            <p>{{ item.label }}{{ item }}</p>                    
        {% endfor %}
    <input type="submit" value="提交"><span style="color: red">{{ error }}</span>
    
    </form>

    渲染方式三

    代码书写极少,封装程度太高,不便于后续的扩展,一般情况下只在本地测试使用

    <h2>通过form自动渲染三</h2>
    <form action="" method="post">
        {{ form.as_p }}
        {#    {{ form.as_table }}#}
        {#    {{ form.as_ul }}#}
    
    </form>

    forms组件之渲染错误信息

    前端渲染代码:(全部采用方式二)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <div class="col-md-6 col-md-offset-3">
        <div class="panel panel-primary">
            <div class="panel-heading">
                <h3 class="panel-title text-center"></h3>
            </div>
            <div class="panel-body">
                <h1 class="text-center">注册</h1>
                <form action="" method="post" novalidate>
                    {% for item in form %}
                        <div class="form-group">
                            <p>{{ item.label }}{{ item }} <span style="color: red">{{ item.errors.0 }}</span></p>
                        </div>
    
                    {% endfor %}
                    <input type="submit" value="提交" class="form-control"><span style="color: red">{{ error }}</span>
    
                </form>
            </div>
        </div>
    </div>
    
    </body>
    </html>
    html页面

    后端views中定义form类:

    from django import forms
    from django.forms import widgets
    
    class RegisterForm(forms.Form):
        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'}))
    
        password = forms.CharField(max_length=8, min_length=3, label='密码',
                                   error_messages={
                                       'max_length': '密码最长为8位',
                                       'min_length': '密码最短为3位',
                                       'required': '密码不能为空'
                                   },
                                   widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    
        re_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
                                      error_messages={
                                          'max_length' : '密码最长为8位',
                                          'min_length' : '密码最短为3位',
                                          'required' : '密码不能为空'
                                      },
                                      widget=widgets.PasswordInput(attrs={'class':'form-control'}))
    
        email = forms.EmailField(label='邮箱',
                                 error_messages={'required': '邮箱不能为空', 'invalid': '邮箱格式不正确'},
                                 widget=widgets.TextInput(attrs={'class':'form-control'}))
    
    
    # views视图函数处理部分:  
    def register(request):
        if request.method == 'GET':
            register_form = RegisterForm()
            return render(request, 'register.html', {'form': register_form})
        else:
            register_form = RegisterForm(request.POST)
            if register_form.is_valid():
                print('校验通过')
                print(register_form.cleaned_data)
                register_form.cleaned_data.pop('re_password')
                models.User.objects.create(**register_form.cleaned_data)
                return HttpResponse('ok')
            else:
                # 校验不通过
                print('校验不通过')
                print(register_form.errors)
    
                return render(request, 'register.html', {'form': register_form})    

    forms组件参数配置

    class Ret(Form):
        name = forms.CharField(max_length=10, min_length=2, label='用户名',
                               error_messages={
                                   'required': '该字段不能为空',
                                   'invalid': '格式错误', 
                                   'max_length': '太长',
                                   'min_length': '太短'},
                               widget=widgets.TextInput(attrs={'class':'form-control'}))

    forms组件局部钩子,全局钩子

    注意点:局部钩子拿什么,校验通过就返回什么。全局钩子拿什么,校验通过返回所有

       -局部钩子
           -def clean_字段名(self):
                -校验规则
                -如果通过,return-如果不通过,抛异常
       -全局钩子(多个字段校验)
            -def clean(self):
                -如果通过,return clean_data
                -如果不通过,抛异常

    局部钩子

     def clean_name(self):  # name字段的局部钩子
            # 获取用户输入的用户名
            name = self.cleaned_data.get('name')
            # 校验名字不能以sb开头
            if name.startswith('sb'):
                # 校验不通过,必须抛异常,
                raise ValidationError('不能以sb开头')
            else:
                # 校验通过,再返回name对应的值
                return name

    全局钩子

      def clean(self):   # 全局钩子
            password = self.cleaned_data.get('password')
            re_password = self.cleaned_data.get('re_password')
            if re_password != password:
                # 校验不通过
                self.add_error('re_password','两次密码不一致')
            else:
                # 局部钩子拿什么返回什么,全局钩子所有都返回
                return self.changed_data

    forms组件总代码

    -使用步骤:
            -写一个类,继承Form类
            -写字段,字段参数(限制该字段的长短)
            -错误信息中文:字段参数
            -widget:控制生成标签的属性
            -视图函数中:
                -实例化得到form对象时,把要校验的数据传入
                -is_valid():clean_data和errors就有值了
                -如果校验通过就存,不通过就给页面提示
           -渲染页面
                -for循环的方式渲染页面(在标签前后可以再加标签)
    from django.db import models
    
    # 创建用户表
    class User(models.Model):
        name = models.CharField(max_length=32,verbose_name='用户名')
        password = models.CharField(max_length=32,verbose_name='密码')
        email = models.EmailField(verbose_name='邮箱')
    模型层models.py
    from django.conf.urls import url
    from app01 import views
    
    urlpatterns = [
        url(r'^register/$',views.register)
    ]
    路由层urls.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>模板层注册页面</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <div class="panel panel-primary">
                    <div class="panel-heading">
                        <h3 class="panel-title text-center"></h3>
                    </div>
                    <div class="panel-body">
                        <h1 class="text-center">注册</h1>
                        <form action="" method="post" novalidate>
                            {% for item in form %}
                                <div class="form-group">
                                    <p>{{ item.label }}{{ item }} <span style="color: red">{{ item.errors.0 }}</span></p>
                                </div>
    
                            {% endfor %}
                            <input type="submit" value="提交" class="btn btn-primary btn-block"><span style="color: red">{{ error }}</span>
    
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
    </body>
    </html>
    模板层register.html

    视图层views中定义form类:

    from django import forms
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    
    class RegisterForm(forms.Form):
        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'
                               }))
    
        password = forms.CharField(max_length=8, min_length=3, label='密码',
                                   error_messages={
                                       'max_length': '密码最长为8位',
                                       'min_length': '密码最短为3位',
                                       'required': '密码不能为空'
                                   },
                                   widget=widgets.PasswordInput(attrs={
                                       'class':'form-control'
                                   }))
    
        re_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
                                      error_messages={
                                          'max_length' : '密码最长为8位',
                                          'min_length' : '密码最短为3位',
                                          'required' : '密码不能为空'
                                      },
                                      widget=widgets.PasswordInput(attrs={
                                          'class':'form-control'
                                      }))
    
        email = forms.EmailField(label='邮箱',
                                 error_messages={
                                     'required': '邮箱不能为空',
                                     'invalid': '邮箱格式不正确'
                                 },
                                 widget=widgets.TextInput(attrs={
                                     'class':'form-control'
                                     
                                     
        def clean_name(self):  # name字段的局部钩子
            # 获取用户输入的用户名
            name = self.cleaned_data.get('name')
            # 校验名字不能以sb开头
            if name.startswith('sb'):
                # 校验不通过,必须抛异常,
                raise ValidationError('不能以sb开头')
            else:
                # 校验通过,再返回name对应的值
                return name
    
        def clean(self):   # 全局钩子
            password = self.cleaned_data.get('password')
            re_password = self.cleaned_data.get('re_password')
            if re_password != password:
                # 校验不通过
                self.add_error('re_password','两次密码不一致')
            else:
                # 局部钩子拿什么返回什么,全局钩子所有都返回
                return self.changed_data
    
    
    
    from app01 import models
    
    
    def register(request):
        if request.method == 'GET':
            # GET请求没有数据,需要生成一个空form对象
            # 这个form跟下面没有关系,是get请求过来的得到一个空form
            register_form = RegisterFrom()
            # 传到前端页面后,通过form进行渲染
            return render(request, 'register.html', {'form': register_form})
    
        else:
            # 实例化得到对象,传入要校验的数据
            register_form = RegisterForm(request.POST)
            if register_form.is_valid():
                # 校验通过,存
                # 取出校验通过的数据
                print('校验通过')
                print(register_form.cleaned_data)
                register_form.cleaned_data.pop('re_password')
                models.User.objects.create(**register_form.cleaned_data)
                return HttpResponse('ok')
    
            else:
                # 校验不通过
                print('校验不通过')
                print(register_form.errors)
              
                return render(request, 'register.html', {'form': register_form})

    forms组件源码分析

    1 为什么局部钩子要写成 clean_字段名,为什么要抛异常
    2 入口在 is_valid()
    3 校验流程
        -先校验字段自己的规则(最大,最小,是否必填,是不是合法)
        -校验局部钩子函数
        -全局钩子校验
        
        
    4 流程
        is_valid()---》return self.is_bound and not self.errors
        self.errors:方法包装成了数据数据
            一旦self._errors有值,就不进行校验了(之前调用过了)
        self.full_clean():核心
            self._errors = ErrorDict()
            if not self.is_bound:  
                return
            self.cleaned_data = {}
            self._clean_fields()
            self._clean_form()
            self._post_clean()
            
            
        self._clean_fields():核心代码,局部钩子执行位置
        
         value = field.clean(value)# 字段自己的校验规则
         self.cleaned_data[name] = value #把校验后数据放到cleaned_data
         if hasattr(self, 'clean_%s' % name): # 判断有没有局部钩子
            value = getattr(self, 'clean_%s' % name)() #执行局部钩子
            self.cleaned_data[name] = value #校验通过,把数据替换一下
           # 如果 校验不通过,会抛异常,会被捕获,捕获后执行
        self.add_error(name, e)
        
        def _clean_form(self):#全局钩子执行位置
        def _clean_form(self):
            try:
                #如果自己定义的form类中写了clean,他就会执行
                cleaned_data = self.clean()
            except ValidationError as e:
                self.add_error(None, e)

    如何看源码

    快速定位到当前py文件

    查看当前py文件中有哪些类,哪些方法

    从来就没有正确的选择,我们只不过是要努力奋斗,使当初的选择变得正确。
  • 相关阅读:
    python数据分析之csv/txt数据的导入和保存
    SQL Server日志文件过大 大日志文件清理方法 不分离数据库
    socket--多进程,多线程服务器
    MYSQL千万级数据表,创建表及字段扩展的几条建议
    常见WEB攻击
    jQuery学习笔记之Ajax用法详解
    redis范围查询应用 数据库 数据库学习 Redis redis范围查询的方法
    CentOS 7下使用RPM安装mysql的方法。
    MySQL Daemon failed to start错误解决办法是什么呢?
    windows无法启动MySQL服务报错1067的解决方法是怎样?
  • 原文地址:https://www.cnblogs.com/gfeng/p/14590589.html
Copyright © 2020-2023  润新知