zoukankan      html  css  js  c++  java
  • 🍖Django之forms组件

    一.Django的forms组件应用场景

    1.应用

    • 一般应用于前端的登入、注册界面, 对用户输入的字段进行校验, 快速的判断用户输入的内容是否合法, 并返回信息

    2.为何不在前端直接使用JS进行校验

    • 前端的校验可以没有, 但后端的校验必须要有
    • 因为前端的校验弱不禁风, 有很多种方式可以伪装成浏览器发送请求传递数据
    • 或者通过爬虫程序绕过前端页面直接朝后端提交数据

    二.forms组件的基本使用(步骤)

    • 导入forms组件
    • 定义一个类, 并继承Form
    • 在类中书写要校验的字段, 字段的属性就是要校验的规则
    • 实例化得到一个Form对象, 把要校验的数据传入
    • 调用[form对象].is_valid( )方法进行校验, 校验通过返回True
    • 校验通过调用[form对象].cleaned_data获得校验后的数据
    • 校验失败调用[form对象].errors获得错误信息

    三.forms组件基本使用示例

    简单的用户注册界面示例

    1.常用字段属性

    属性 释义
    min_length 最小长度
    max_length 最大长度
    require 该字段必须得填
    label 该字段的标签

    2.组件文件 form.py

    • 建议在应用文件夹下创建一个专门存放forms组件的文件 (不创建直接写在views.py中也可以)

    • 导入组件并创建类

    from django import forms
    
    
    class RegisterForm(forms.Form):
        # 校验name字段,最大长度为8,最短为3,下面的字段类似
        name = forms.CharField(max_length=8, min_length=3, lable='用户名')
        password = forms.CharField(max_length=10, min_length=4, lable='密码')
        re_password = forms.CharField(max_length=10, min_length=4, lable='确认密码')
        email = forms.EmailField(lable='邮箱')
    

    3.视图层 views.py 文件

    • 实例forms对象, 使用对方法进行校验
    from app01.myform import RegisterForm
    
    
    def register(request):
        if request.method == 'POST':
            # 实例得到form对象,将需要校验的数据传入(数据可以是前端传过来的数据,也可以是自己后端已有的数据)
            # dic = {'name':'shawn','password':'1111','re_password':'1111','email':'123@qq.com'}
            # form_data = RegisterForm(dic)  # 示例:直接将已有的数据传入
            form_data = RegisterForm(request.POST)  # 将前端post请求的数据传入
    
            # 进行校验 is_valid()
            if form_data.is_valid():
                print('校验成功')
                # 获得校验成功的数据 cleaned_data
                print(form_data.cleaned_data)
                return HttpResponse('校验成功')
            else:
                print('检验失败')
                # 获得检验失败的错误提示 errors
                print(form_data.errors)
    
        # forms组件可以自动生成HTML代码,因此可以直接在页面上进行显示
        form_data = RegisterForm()  # 先产生form空对象,再将其传给前端
        return render(request, 're_form.html', {'form': form_data})
    

    ps : cleaned_data属性必须是经过is_valid( )校验之后才产生的, 在校验之前使用会报错 : 属性不存在

    4.路由层 urls.py 文件

    re_path('^re_form/', views.register)
    

    5.模板层 re_form.html 文件

    <div class="container">
        <div class="row">
            <div class="col-md-4">
                <div class="panel panel-info">
                    <div class="panel-heading">注册页面</div>
                    <div class="panel-body">
                        <form action="" method="post">
                            <p>用户名:{{ form.name }}</p>
                            <p>密码:{{ form.password }}</p>
                            <p>确认密码:{{ form.re_password }}</p>
                            <p>邮箱:{{ form.email }}</p>
                            <br>
                            <input type="submit" class="btn btn-block btn-success" value="注册">
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
    

    6.测试效果

    • 检验失败展示

    • 检验成功展示

    四.forms组件之渲染标签方式

    1.自动渲染方式一

    • 就是上面示例中的写法
    • 可扩展性很强, 但是需要书写的代码太多
    <form action="" method="post">
        <p>用户名:{{ form.name }}</p>
        <p>密码:{{ form.password }}</p>
        <p>确认密码:{{ form.re_password }}</p>
        <p>邮箱:{{ form.email }}</p>
        <br>
        <input type="submit" class="btn btn-block btn-success" value="注册">
    </form>
    
    • 也可以使用label标签替代前缀(''用户名''、''密码''等等)
    <form action="" method="post">
        <p>{{ form.name.label }}:{{ form.name }}</p>
        <p>{{ form.password.label }}:{{ form.password }}</p>
        <p>{{ form.re_password.label }}:{{ form.re_password }}</p>
        <p>{{ form.email.label }}:{{ form.email }}</p>
        <br>
        <input type="submit" class="btn btn-block btn-success" value="注册">
    </form>
    

    2.自动渲染方式二(常用方式)

    • 使用for循环
    • 代码书写简单, 并且扩展性也高 (推荐使用)
    <form action="" method="post" novalidate>
        {% for foo in form %}
            <div class="form-group">
                {# 循环取出 name,password,re_password,email #}
                <p>{{ foo.label }}:{{ foo }}</p>
            </div>
        {% endfor %}
    	<input type="submit" class="btn btn-block btn-success" value="注册">
    </form>
    

    3.自动渲染方式三(不常用)

    • 代码书写极少, 封装程度太高, 不便于后续的扩展, 一般情况下只在本地测试使用
    <form action="" method="post" novalidate>
        {{ form.as_p }}       {# 与上两种显示方式相同 #}
        {{ form.as_ul }}      {# 前面带点 #}
        {{ form.as_table }}   {# 在一行显示 #}
        <input type="submit" class="btn btn-block btn-success" value="注册">
    </form>
    

    image-20210327191311016

    五.forms组件之渲染错误信息

    1.字段参数(举例)

    • required : 是否允许为空,默认False
    • error_messages : 自定义错误信息
    • widget : 使用HTML插件
      • TextInput
      • PasswordInput

    2.代码演示

    • myform.py 文件
    from django import forms
    from django.forms import widgets
    
    class RegisterForm(forms.Form):
        # 校验name字段,最大长度为8,最短为3,下面的字段类似
        name = forms.CharField(max_length=8, min_length=3, label='用户名',
                               # 自定义错误信息
                               error_messages={
                                   'min_length': '用户名最少3位!',
                                   'max_length': '用户账户最大8位',
                                   'required': '用户名不能为空!',
                               },
                               # 添加一个form-control类
                               widget=widgets.TextInput(attrs={'class':'form-control'}))
        password = forms.CharField(max_length=10, min_length=4, label='密码',
                                   error_messages={
                                       'min_length': '密码最少4位!',
                                       'max_length': '密码最大10位',
                                       'required': '密码不能为空!',
                                   },
                                   widget=widgets.PasswordInput(attrs={'class':'form-control'}))
        re_password = forms.CharField(max_length=10, min_length=4, label='确认密码',
                                      error_messages={
                                          'min_length': '密码最少4位!',
                                          'max_length': '密码最大10位',
                                          'required': '密码不能为空!',
                                      },
                                      widget=widgets.PasswordInput(attrs={'class':'form-control'}))
        email = forms.EmailField(label='邮箱',
                                 error_messages={
                                     'invalid': '邮箱格式不正确',
                                     'required': '邮箱不能为空',
                                 },
                                 widget=widgets.TextInput(attrs={'class':'form-control'}))
    
    • views.py 文件
    from app01.myform import RegisterForm
    
    
    def register(request):
        if request.method == 'POST':
            form_data = RegisterForm(request.POST)  # 将前端post请求的数据传入
    
            if form_data.is_valid():
                print(form_data.cleaned_data)
                return HttpResponse('校验成功')
            else:
                # 将带有错误信息的form对象返回前端页面
                return render(request, 're_form.html', {'form': form_data})
    
        form_data = RegisterForm()  # 将form对象传给前端
        return render(request, 're_form.html', {'form': form_data})
    
    • re_form.html 文件
    <form action="" method="post" novalidate>
        {% for foo in form %}
            {# 在后方设置一个span标签用来放错误提示 #}
            <p>{{ foo.label }}:{{ foo }}
                <span style="color: red">{{ foo.errors.0 }}</span>
            </p>
        {% endfor %}
        <input type="submit" class="btn btn-block btn-success" value="注册">
    </form>
    

    ps : <form action="" method="post" novalidate> : form 表单标签内的 novalidate表示前端不进行校验

    3.效果展示

    六.forms组件之全局钩子, 局部钩子

    1.钩子函数

    • 全局和局部钩子都是定义在forms组件之内的函数
    • 钩子函数类似于校验的第二道关卡, 能够让我们自定义校验规则

    2.两种钩子函数

    • 局部钩子 : 用于给单个字段增加校验规则
    • 全局钩子 : 用于给多个字段增加校验规则

    3.两种钩子函数的书写方式

    • 局部钩子
    def clean_字段名(self):
        字段值 = self.cleaned_data.get('字段名')
        [判断逻辑代码]
        [不通过抛出 ValidationError 异常]
        [通过则返回该字段值]
    
    • 全局钩子
    def clean(self):
        字段值1 = self.cleaned_data.get('字段1')
        字段值2 = self.cleaned_data.get('字段2')
        [判断逻辑代码]
        [不通过抛出 ValidationError 异常]
        [通过则 return self.cleaned_data]
    

    4.钩子示例

    • 需求一 : 校验用户名不能有敏感字眼
    • 需求二 : 校验两次输入的密码需一致

    myform.py 新增钩子函数

    # 局部钩子
    def clean_name(self):
        name = self.cleaned_data.get('name')
        for i in ['爱德华','sb']:
            if i in name:
                raise ValidationError(f'用户名中包含敏感词汇:"{i}"')
        else:
            return name
    
    # 全局钩子
    def clean(self):
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if password == re_password:
            return self.cleaned_data
        else:
            raise ValidationError('两次密码不一致')
    

    views.py 文件

    from app01.myform import RegisterForm
    
    
    def register(request):
        if request.method == 'POST':
            form_data = RegisterForm(request.POST)  # 将前端post请求的数据传入
    
            if form_data.is_valid():
                return HttpResponse('校验成功')
            else:
                # 获取到 ValidationError 中的 error 信息传给前端
                error = form_data.errors.get('__all__')
                return render(request, 're_form.html', {'form': form_data,'error':error})
    
        form_data = RegisterForm()  # 将form空对象传给前端
        return render(request, 're_form.html', {'form': form_data})
    

    re_form.html 文件

    <form action="" method="post" novalidate>
    {% for foo in form %}
        <p>{{ foo.label }}:{{ foo }}
            <span style="color: red; display:inline-block">{{ foo.errors.0 }}</span>
        </p>
    {% endfor %}
    
    <input type="submit" class="btn btn-block btn-success" value="注册">
    {# 判断错误信息是否为None,是None就不渲染error #}
    {% if error is None %}
        <span style="color: red; display:inline-block"></span>
    {% else %}
      <span style="color: red; display:inline-block">{{ error }}</span>
    {% endif %}
    </form>
    

    需求一演示 :

    需求二演示 :

    七.error错误提示为何是ul标签分析

    • 上面我们在 views.py 文件中写到的,教研室表打印错误提示信息
    else:
        print('检验失败')
        # 获得检验失败的错误提示 errors
        print(form_data.errors)
    
    • 查看打印结果 :

    image-20210328000932090

    为什么是一个ul标签格式, 一定是重写了 __str__方法

    • 我们导入 ErrorDict 来查看内部源码
    from django.forms.utils import ErrorDict
    

    image-20210328001258090

    发现其内部重写了__str__方法, 返回的是 as_ul方法, 该方法内部就是定义了html的格式并返回

    并且还有一些其他的方法 : 比如 json 格式, data 数据, text 格式等等

    而返回 as_ul 方法则是为了方便渲染模板

    八.常用字段及插件

    1. 初始值 : initial

    username = forms.CharField(
            min_length=8,
            label="用户名",
            initial="shawn"  # 设置默认值
        )
    

    2. 自定义错误提示 : error_massages

    username = forms.CharField(
            min_length=8,
            label="用户名",
            initial="shawn",
            error_messages={
                "required": "不能为空",
                "invalid": "格式错误",
                "min_length": "用户名最短8位"
            }
        )
    

    3. 密码框 : password

    # 设置render_value在输入错误的时候, 保留输入的内容value值
    pwd = forms.CharField(
            min_length=4,
            label="密码",
            widget=forms.widgets.PasswordInput(attrs={'class': 'a1'}, render_value=True)
        )
    

    4. 反选radio

    gender = forms.fields.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "未知")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )
    

    5. 单选checkbox

    keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )
    

    6. 多选checkbox

    Copyhobbies1 = forms.MultipleChoiceField(
        choices=((1, "read"), (2, "run"), (3, "play"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )
    

    7. 单选Select

    Copyeducation  = forms.ChoiceField(
        choices=((1, "高中"), (2, "中专"), (3, "大专"), (4, "本科"), (5, "研究生"), (6, "硕士")),
        label="学历",
        initial=3,
        widget=forms.widgets.Select(attrs={'class': 'form-control'})
    )
    

    8. 多选Select

    Copyhobbies2 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
            label="爱好",
        initial=[1, 2],
        widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'})
    )
    

    9.更多插件(了解)

    插件名称 对应input
    TextInput input type="text"
    PasswordInput input type="password"
    HiddenInput input type="hidden"
    NumberInput input type="number"
    EmailInput input type="email"
    URLInput input type="url"
    Textarea textarea
    DateInput input type="data"
    DateTimeInput input type="datetime"
    TimeInput 只获取时分秒
    CheckboxInput input type="checkbox"
    CheckboxSelectMultiple 多选按钮
    Select select框
    NullBooleanSelect select框 三个选项 0 1 null
    SelectMultiple 可多选的select框
    RadioSelect input type="radion"
    FileInput 可以查看当前目录下的所有文件。作用不大
  • 相关阅读:
    lombok、japidocs、swagger学习
    mysql数据库添加新用户,并授予所有权限
    局域网内共享自己的数据库
    HttpURLConnection模板
    nginx简单学习总结
    redis常见命令
    mybatis中的#{}和${}的区别
    Python的多线程
    使用python发送邮件
    python操作MySQL数据库
  • 原文地址:https://www.cnblogs.com/songhaixing/p/14589384.html
Copyright © 2011-2022 走看看