8.Djang 之 forms组件

1. form组件引入

写一个注册功能
获取用户名密码,利用form表单提交.
在后端判断用户名字和密码是否符合一些条件.
用户名不能含有 xx,密码不能少与三位.
如果不符合要求将信息展示到前端页面中.
* span 行级标签 字符有多则占多大的空间,空字符则看不出.
    # 1. 用户登入
    url('^register/', views.register)
# 0. 用户注册
def register(request):

    # 0.1 返回注册页面
    return render(request, 'register.html')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
    <!--0. 动态获取静态资源目录名称-->
    {% load static %}
    <!--1. 导入 jQuery js 文件-->
    <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
    <!--2. 导入 bootstrap css 文件-->
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    <!--3. 导入 bootstrap js 文件-->
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
    <style>
        span {
            color: red;
        }
    </style>
</head>
<body>

<div>
    <!-- 4. form表单 -->
    <form action="" method="post">
        <p>
            <label for="username">名称:
                <input type="text" id="username" name="username">
                <span id="sp1"></span>
            </label>
        </p>
        <p>
            <label for="password">密码:
                <input type="password" id="password" name="password">
                <span id="sp2"></span>
            </label>
        </p>
        <p><input type="submit"></p>
    </form>
</div>
<script>
    // 5. 绑定事件
    $(':submit').on('click', function () {
        // 6. 获取表单中的数据
        var username = $(':text').val()
        var password = $(':password').val()

        // 7. 禁止后续事件 禁止submit的提交
        event.preventDefault()

        // 8. ajax 请求
        $.ajax({
            url: '',
            type: 'post',
            data: {username: username, password: password},


            success: function (args) {
                if (args.username_bug === '✓') {
                    $('#sp1').text('✓')
                } else {
                    $('#sp1').text(args.username_bug)

                }
                if (args.password_bug === '✓') {
                    $('#sp2').text('✓')
                } else {
                    $('#sp2').text(args.password_bug)

                }
            }
        })

    })
</script>
</body>
</html>
from django.shortcuts import render


# Create your views here.

# 0. 用户注册
def register(request):
    # 0.2 判断 ajax请求
    if request.is_ajax():
        # 0.3 获取ajax的谁
        username = request.POST.get('username')
        password = request.POST.get('password')
        print(username,password)

        # 0.4 组织返回的数据格式
        back_dict = {'code': 200, 'username_bug': '√', 'password_bug': '√'}
        # 0.5 判断用户名与密码是否符合条件
        if 'xx' in username:
            back_dict['username_bug'] = '用户名不能含有xx'
        if not username:
            back_dict['username_bug'] = '用户名称不能为空'

        if 3 > len(password):
            back_dict['password_bug'] = '用户密码不能少于三位'
        if not password:
            back_dict['password_bug'] = '用户密码不能为空'

        # 0.6 返回json格式字符串
        from django.http import JsonResponse
        print(back_dict)
        return JsonResponse(back_dict)

    # 0.1 返回注册页面
    return render(request, 'register.html')

2. forms组件

forms组件的主要功能:

1.生成页面可用的HTML标签
2.后端对获取数据进行校验         数据校验
3.数据不符要求在前端展示         展示错误提示信息  保留上次输入内容
ps:
数据校验前端可有可无,后端必须有,全端页面的校验功能不完善.
能利用爬虫程序直接绕开前端页面向后端提交数据.

例:
购物网站
选中货物之后会计算价格发送给后端,价格的值可能被篡改,如果后端不做价格校验···

实际中获取到用户选择的商品的主键值,后端查询到的价格后在计算一次
如果前后端计算结果一致,完成支付,否则直接拒绝.

一般会进行前后端双重验证,保险、
2.1 导入组件
写在views.ps 
# 1. forms 组件
# 1.1 导入组件
from django import forms
# 1.2 定义类
class MyForm(forms.Form):
    # 1.3 用户名密码     字符类型     min_length 最小长度  max_length 最大长度
    username = forms.CharField(min_length=3, max_length=8)
    password = forms.CharField(min_length=3, max_length=8)
    # 1.4 邮箱格式 xxx@qq.com
    email = forms.EmailField()
2.2 检验数据
1. tests.py        中测试
2. pycharm Console 中测试
form对象 = views.MaForm(测试的数据-->dict)   

可以多传,多传的字段直接被忽略.
不能少传,必须给存在的字段写入值.
	
对象.is_valid()     判断数据是否全部合法,返回布尔值.
对象.cleaned_data   清理不符合的数据,也就就符合要求的数据.
对象.errors         不符合的数据及错误的原因
# app01 应用下 tests.py
from django.test import TestCase

# Create your tests here.
import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django7_2.settings")
    import django

    django.setup()

    # 0. 导入 视图层
    from app01 import views

    username = input('username>>>:').strip()
    password = input('password>>>:').strip()
    email = input('email>>>:').strip()

    # 1. 实例化对象
    form_obj = views.MyForm({'username': username, 'password': password, 'email': email})

    # 2. 判断数据是否符合要求
    print(f'数据是否符合要求>>>:{form_obj.is_valid()}')

    # 3. 过滤出符合要求的数据  --> dict
    print(f'符合要求的数据>>>:{form_obj.cleaned_data}')

    # 4. 查看不符合要求的数据和原因
    print(f'不符合要求的数据>>>:{form_obj.errors}')
tests.py中
存在的字段必须给参数.

2

python Console
多出的参数被忽略

4

2.3 渲染标签
forms组件只会渲染获取用户输入的便签(input select radio checkbox)
不会渲染提交按键
    # 2. 用户注册 forms 组件
    url('^register_forms/', views.register_forms)
# 2. 注册 + forms组件
def register_forms(request):
    # 2.1 生成对象
    forms_obj = MyForm()

    # 2.2 返回页面 & forms对象
    return render(request, 'register_forms.html')
第一种渲染方式:
代码书写极少,封装程度太高,不便于后续的拓展,一般情况下只在本地测试使用.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> forms组件 使用方式1 </title>
    
</head>
<body>
    {{ forms_obj.as_p }}
    <br>
    <br>
    {{ forms_obj.as_ul }}
    <br>
    <br>
    {{ forms_obj.as_table}}
</body>
</html>

5

第二种渲染方式: 
可扩展性强,但书写代码太多
label属性默认展示的字段名称,也可以自己修改,直接在类中为label属性设置参数.

email = forms.EmailField(label='邮箱')

form_obj.username.label 获取属性的名称,如果没有设置就拿字段名称.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> forms组件 使用方式2 </title>

</head>
<body>

    {{ forms_obj.username }}{{ forms_obj.password }}
    <br>
    <br>
    <p>{{ forms_obj.username.label }} :{{ forms_obj.username }}</p>
    <p>{{ forms_obj.password.label }} :{{ forms_obj.password }}</p>
    <p>{{ forms_obj.email.label }} :{{ forms_obj.email }}</p>

</body>
</html>

1

第三种渲染方式: (推荐)
代码简单扩展性高
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>form组件获取输入</title>
</head>
<body>
<form action="" method="post">
    <p></p>
    {% for form in form_obj %}
        <p>{{ form.label }}:{{ form }}</p>
    {% endfor %}
    <input type="submit" class="btn btn-info">
    
</form>

</body>
</html>

2

2.4 渲染错误信息
在from表单中点击提交会触发前端的数据检验,但是前端的检验能被篡改,
在form标签中添加 novalidate 属性,让浏览器不做校验.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> forms组件 使用方式3 </title>

</head>
<body>
<form action="" method="post">
    {% for form in forms_obj %}
        <p>{{ form.label }}:{{ form }}</p>
        <span style="color: red">{{ form.errors }}</span>
    {% endfor %}

    <input type="submit">
</form>
</body>
</html>

4

在后端做数据校验:

1. 获取前端POST请求数据
2. Forms对象(字典的格式数据) 
request.POST可以看成一个字典
# 2. 注册 + forms组件
def register_forms(request):
    # 2.1 生成对象
    forms_obj = MyForm()

    # 2.2 判断请求方式
    if request.method == 'POST':
        # 2.3 获取POST数据
        print(request.POST)  # 看成是一个字典
        
        # 2.4 检验数据
        forms_obj = MyForm(request.POST)

    # 返回页面 & forms对象
    return render(request, 'register_forms.html', locals())
request.POST的数据
<QueryDict: {'username': ['kid'], 'password': ['123'], 'email': ['1360012768@qq.com']}>
必备条件 get请求和post传给html的变量名必须一样,共有用一个渲染方式.

6

forms组件当数据不合法的情况下,会保存你上次的数据,让用户可以基于之前的结果进行修改.

7

{{ form.errors }} 获取的是ul>li嵌套标签
{{ form.errors.0 }} 获取的是ul>li标签中问文本信息

8

前端错误信息是英文的,自定义中文错误信息.
error_messages中设置

min_length 不符合最小位数对应的报错信息
max_length 不符合最大位数对应的报错信息(几乎不会触发)
invalid    不符合邮箱格式对应的报错信息
required   不输入信息对应的报错信息
# 1.2 定义类
class MyForm(forms.Form):
    # 1.3 用户名密码     字符类型     min_length 最小长度  max_length 最大长度
    username = forms.CharField(min_length=3, max_length=8,
                               error_messages=
                               {
                                   'min_length': '用户名不能少于3位数!',
                                   'max_length': '用户名不能大于8位数!'
                               })
    password = forms.CharField(min_length=3, max_length=8,
                               error_messages={
                                   'min_length': '密码不能少于3位数!',
                                   'max_length': '密码不能超过8位数!'
                               })
    # 1.4 邮箱格式 xxx@qq.com
    email = forms.EmailField(
        error_messages={
            'invalid': '邮箱格式不正确',
            'required': '邮箱不能为空'
        }
    )
调整一下span标签的位置.报错信息和输入款在一个p标签中.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> forms组件 使用方式3 </title>
    <!--0. 动态获取静态资源目录名称-->
    {% load static %}
    <!--1. 导入 jQuery js 文件-->
    <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
    <!--2. 导入 bootstrap css 文件-->
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
    <!--3. 导入 bootstrap js 文件-->
    <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>

</head>
<body>
<form action="" method="post" novalidate>
    {% for form in forms_obj %}
        <p>{{ form.label }}:{{ form }} <span style="color: red">{{ form.errors.0 }}</span></p>
    {% endfor %}

    <input type="submit">
</form>

</body>
</html>
密码为空

9

数据不符合要求.

10

max_length设置之后,不篡改前端代码.
在输入框输入数据的时候不能超过这个限制.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4NX4re5Y-1647596259671)(C:\Users\13600\Desktop\11.png)]

2.4 钩子函数(HOOK)
在特定的节点自动触发完成响应操作.
钩子函数在forms组件中类似于第二道关卡,能够设置自定义校验规则.
在forms组件中有两类钩子
1. 局部钩子
	获取单个字段的数据,增加额外的校验规则.
2. 全局钩子
	获取全部字段的数据,为多字段增加校验规则.
钩子函数只有在通过了字段中设置的规则,才能触发对应该字段的钩子函数.
第一层规则过了的数据会被打包cleaned_data中.
第二层则是通过钩子函数将cleaned_data中的数据取出来在作判断.
	通过则直接将勾取的数据返回.
	不通过则通过 .add_error('字段', '错误信息') 设置错误的提示.
例题:
1.校验用户名只能是(数字 字母 下划线的组合)     只校验 username 字段 单个字段  使用局部钩子
2.检验密码和确认密码是否一致                  校验 password confirm 多个字段 使用全局钩子
# 1.2 定义类
class MyForm(forms.Form):
    
    # --------------------------第一层规则-------------------------------
    
    # 1.3 用户名密码     字符类型     min_length 最小长度  max_length 最大长度
    username = forms.CharField(min_length=3, max_length=8, label='名称',
                               error_messages=
                               {
                                   'min_length': '用户名不能少于3位数!',
                                   'max_length': '用户名不能大于8位数!',
                                   'required': '用户名不能为空'
                               })
    # 1.4 用户密码    字段类型
    password = forms.CharField(min_length=3, max_length=8, label='密码',
                               error_messages={
                                   'min_length': '密码不能少于3位数!',
                                   'max_length': '密码不能超过8位数!',
                                   'required': '密码不能为空'
                               })
    # 1.5 确认密码
    confirm = forms.CharField(min_length=3, max_length=8, label='确认密码',
                              error_messages={
                                  'min_length': '密码不能少于3位数!',
                                  'max_length': '密码不能超过8位数!',
                                  'required': '密码不能为空'
                              })
    # 1.6 邮箱格式 xxx@qq.com
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                 'invalid': '邮箱格式不正确',
                                 'required': '邮箱不能为空'
                             }
                             )

    
    # --------------------------第二层规则-------------------------------

    # 1.7 局部钩子 clean_字段名
    def clean_username(self):
        # 1.7.0 获取字段的值
        username = self.cleaned_data.get('username')
        # 1.7.1 增加额外的规则  只能是字母数字下划线的组合,只能是字母开头
        import re
        res = re.findall(r'^[a-zA-Z]([a-zA-Z0-9_]{3,8})$', username)

        # 返回一个列表,不有匹配到数据就不为空
        print(res)
        # 1.7.2 没有匹配到数据就说明用户名称不符合要求,为字段添加错误信息
        if not res:  # 两个参数 第一个参数是字段 第二次参数是 错误的信息
            self.add_error('username', '只能是字母数字下划线的组合,只能是字母开头!')
        return username

    # 1.8 全局钩子 获取全部的字段信息
    def clean(self):
        # 1.8.0 获取字段的信息
        password = self.cleaned_data.get('password')
        confirm = self.cleaned_data.get('confirm')

        # 增加额外的规则
        # 1.8.1 对密码进行检验
        import re
        res = re.findall(r'^[a-zA-Z]([a-zA-Z0-9_]{3,8})$', password)
        if not res:  # 两个参数 第一个参数是字段 第二次参数是 错误的信息
            self.add_error('password', '只能是字母数字下划线的组合,只能是字母开头!')

        # 1.8.2 两次密码一致校验
        if password != confirm:  # 两次密码不一直为 confirm二次确认密码添加错误信息
            self.add_error('confirm', '两次密码不一致!')

        # 1.8.3 返回所有的数据
        return self.cleaned_data

image-20220318004839550

2.5 所有字段及参数
针对不同的input框有不同的字段类型和标签样式.

1.针对不同的类型input修改
	text     普通文本(默认)  
	password 密文
	date     日期
	···
	
2.参数 
label                   字段名
error_messages          自定义报错信息
initial  设置默认值      也可以在在样式中设置value
required 控制字段是否必填 required=False 可以不填


3. 字段设置样式 多个属性,空格隔开
widget = forms.widget.TextTnput() 普通文本
widget = forms.widget.PasswordTnput() 密文
widget = forms.widget.EmailTnput() 邮箱
widget = forms.widget.TextTnput(attrs={'class':'form-control c1 c2', 'value':'kid'}) 
1. text文本框
字段类型 CharField 对应文本参数 TextInput
# 3. 定义类
class MyForm2(forms.Form):
    # 3.1 用户名
    username = forms.CharField(
        label='名称',
        # input 框内的值
        initial='字母数字下划线的组合,只能是字母开头',

        # 第一层规则
        min_length=3, max_length=8,
        # 错误提示
        error_messages={'min_length': '名称至少3位!', 'max_length': '名称最多8位!'},

        # 样式
        widget=forms.widgets.TextInput(
            attrs={'class': 'form-control', 'style': 'color: green;'}),
    )

image-20220318032403106

image-20220318034822936

<QueryDict: {'username': ['kid']}>
2. password密文
字段类型 CharField 对应密文参数 PasswordInput
    # 3.2 密码
    password = forms.CharField(
        label='密码',
        initial='不要太简单',
        # 第一层规则
        min_length=3, max_length=8,
        # 错误提示
        error_messages={
            'min_length': '密码至少3位',
            'max_length': '密码最长8位'},

        # 样式
        widget=forms.widgets.PasswordInput(
            attrs={'class': 'form-control'}), )

image-20220318033103987

    # 3.2 密码
    password = forms.CharField(
        label='密码',
        # initial='不要太简单',
        # 第一层规则
        min_length=3, max_length=8,
        # 错误提示
        error_messages={
            'min_length': '密码至少3位',
            'max_length': '密码最长8位'},

        # 样式
        widget=forms.widgets.PasswordInput(
            attrs={'class': 'form-control', 'value': '不要太简单'}), )

image-20220318033531843

image-20220318034938859

<QueryDict: {'password': ['不要太简单']}>
3. 邮箱
字段类型 EmailField 对应邮箱参数 EmailInput

invalid 邮箱地址无效 设置的报错信息
    # 4. 邮箱
    email = forms.EmailField(
        label='邮箱',
        error_messages={
            'invalid': '邮箱格式不正确',
            'required': '邮箱不能为空'},
        widget=forms.widgets.EmailInput(
            attrs={'style': 'color:red;'}
        )
    )

image-20220318034119103

<QueryDict: {'email': ['1360012768@qq.com']}>
4. 单选框 radio
字段类型 ChoiceField 对应单选框参数 RadioSelect

choices=(('提交的值', "显示的值"),) 提供可选的值
initial 设置默认选中的值
    # 3.5 单选框 radio
    gender = forms.fields.ChoiceField(
        label="性别",
        # -       提交的值 显示的值
        choices=(('male', "男"), ('female', "女"), ('secret', "保密")),
        # 设置默认选中
        initial='secret',
        widget=forms.widgets.RadioSelect())

image-20220318035448422

<QueryDict: {'gender': ['female']}>
5.下拉框
字段类型 ChoiceField 对应下拉框参数 Select

choices=(('提交的值', "显示的值"),) 提供可选的值
initial 设置默认选中的值
    # 3.6 下拉款 select
    course = forms.ChoiceField(
        label='课程',
        choices=(('Python', 'Python'), ('Linux', 'Linux'), ('MySQL', 'MySQL')),
        initial='MySQL',
        widget=forms.widgets.Select())

image-20220318040154208

<QueryDict: {'course': ['MySQL']}>
6.多选框单选
字段类型 ChoiceField 对应多选框单选参数 CheckboxInput
* 这东西有问题
    # 3.7 单选 checkbox
    protocol = forms.ChoiceField(
        label='xxx协议',
        initial='checked',
        widget=forms.widgets.CheckboxInput())

3

7.多选框
字段类型 MultipleChoiceField 对应下多选框参数 CheckboxSelectMultiple

choices=(('提交的值', "显示的值"),) 提供可选的值
initial 设置默认选中的值
    # 3.8 多选 checkbox
    hobby = forms.MultipleChoiceField(
        label='兴趣',
        choices=(('web', 'web开发'), ('reptile', '爬虫'), ('financial_quantification', '金融量化')),
        initial=[1, 2],
        widget=forms.widgets.CheckboxSelectMultiple())

4

8.手机正则匹配
字段类型 CharField 对应正则参数 RegexValidator

RegexValidator(r'正则匹配', '错误提示')
    #  正则模块
    from django.core.validators import RegexValidator

    # 3.9 正则匹配
    phone = forms.CharField(
        label='号码',
        validators=[  # -       正则表达      提示
            RegexValidator(r'^[0-9]+$', '输入的必须是数字'),
            RegexValidator(r'^159[0-9]+$', '号码必须以159开头'),
        ]
    )

6

9.数字
IntegerField(Field)
  max_value=None,       最大值
  min_value=None,       最小值
  
FloatField(IntegerField)
  ...
DecimalField(IntegerField)
  max_value=None,        最大值
  min_value=None,        最小值
  max_digits=None,       总长度
  decimal_places=None,   小数位长度
    # 3.10 
    number = forms.IntegerField(
        min_value=1,
        max_value=6,
        error_messages={
            'min_value': '最小只能是1',
            'max_value': '最大只能是6'
        }
    )

8

3. forms组件源码

切入点在form_obj.is_valid()
   def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return self.is_bound and not self.errors
	# 如果is_avlid要返回True 那么self.is_bound要为True  self.errors 为False
    # 只要传的值不为空就是 True
    # self.is_bound = data is not None or files is not None  
    
    
  
    @property
    def errors(self):
        "Returns an ErrorDict for the data provided for the form"
        # self._errors = None 默认就是	None 百分百走 self.full_clean()
        if self._errors is None:
            self.full_clean()
        return self._errors

    # forms组件所有功能都出自于该方法
    def full_clean(self):
		···
        self._clean_fields()  # 检验字典 + 局部钩子
        self._clean_form()    # 全局钩子
        self._post_clean()    # 用不到
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值