Django框架之form组件详解

form组件:

前戏:

# 我们先来实现一个注册功能来引出什么是form组件
需求:获取用户名和密码:
      利用form表单提交数据
      在后端判断用户名和密码是否符合一定的格式条件

格式条件:
        用户名不能有特殊符号
        密码不能少于六位...
        
# 如果不符合条件需要将提示信息展示到前端页面

这要怎么实现呢? 我们来尝试一下:

视图层:views.py

def ab_form(request):
    back_dic = {'username':'','password':''}             # 定义一个空的字典(用来前端处理GET请求数据)
    if request.method == 'POST':			 # 判断post请求
        username = request.POST.get('username')          # 取到用户输入的username
        password = request.POST.get('password')          # 取到用户输入的password
        if '傻逼' in username:                           # 判断是否存在'傻逼'不合法字符
            back_dic['username'] = '用户名不合法'         # 如果不合法则把提示信息添加到字典中
        if len(password) < 6:                            # 判断密码是否小于6
            back_dic['password'] = '密码不能少于六位数'
    return render(request,'ab_form.html',locals())       # 返回到前端页面

前端页面

<form action="" method="post">   // 记得修改method属性
    <p>username:
        <input type="text" name="username">
        <span style="color: darkred">
            {{ back_dic.username }}   、
            // 无论是post请求还是get请求,页面都能够获取到字典 只不过:
            // 如果提交的是GET请求这个span标签是没有数据的(字典是空的)
            // 如果提交的是post请求才可能有数据
        </span>
    </p>
    <p>password: <input type="password" name="password">
        <span style="color: darkred">
            {{ back_dic.password }}	  // 取出后端传的数据(同上)
        </span>
    </p>
    <input type="submit" class="btn btn-success">
</form>

总结:

1.手动书写前端获取用户数据的html代码		 # 渲染html代码
2.后端对用户数据进行校验				 # 校验数据
3.对不符合要求的数据进行前端提示			 #  展示提示信息

补充:

# 数据的校验必须在后端执行,不能在前端利用js直接完成:
数据校验前端可有可无
但是后端必须要有!

因为前端的校验是弱不禁风的 他人可以直接修改,或者利用爬虫程序绕过前端页面直接朝后端提交数据

# 举例:
购物网站	
选取了货物之后 会计算一个价格发送给后端进行结算,如果后端不做价格的校验,用户通过部分手段修改了价格,修改了1元,后端在不校验的话,那么这样可想而知...

# 正规的编写方式
实际是获取到用户选择的所有商品的主键值
然后在后端通过主键值查询出所有商品的价格 再次计算一遍
如果跟前端一致 那么完成支付如果不一致直接拒绝

forms组件

# forms组件
	能够完成的事情
		1.渲染html代码
		2.校验数据
		3.展示提示信息

基本使用:

from django import forms  # 导入forms组件

# 定义一个类继承forms.Form
class MyForm(forms.Form):
    # username字符串类型最小三位最长8位
    username = forms.CharField(min_length=3,max_length=8)
    # password字符串类型最小三位,最长12位
    password = forms.CharField(min_length=3,max_length=12)
    # email字符必须符合邮箱格式:xxx@xx.com
    email = forms.EmailField()

测试环境:

# pycharm为我们提供了一个测试django的测试环境:
	在这个里面就可以选择性的测试Django里面任意一个py文件
        位置:pycharm下方 >> Python Console
    
# 但是提示功能不是很全:需要自己写(不提示不要认为是错了)

校验数据关键字:

# 测试环境下:

from app01 import views

form_obj = views.MyForm({'username':'gary','password':123456,'email':'qwer'})
# 给类传一个字典的形式,那么它会按照类里面写的这些字段去校验这些数据。

# 那么如何查看这些数据是否合法呢?

is_valid()

# 判断是否合法:返回结果为布尔值
# 示例:
form_obj.is_valid()
返回结果:False

# 注意:该方法只有在所有数据全部合法的情况下才会返回True

cleaned_data

# 查看所有校验通过的数据
# 示例:
form_obj.cleaned_data
返回结果:{'username': 'gary', 'password': '123456'}

errors

# 查看所有不符合校验规则以及不符合的原因
# 示例:
form_obj.errors
返回结果:{'email': ['Enter a valid email address.']}

验证多传数据是否进行校验

form_obj = views.MyForm({'username':'gary','password':'123','email':'123@qq.com','hobby':'study'})   # 多传一个字段
form_obj.is_valid()  # 判断是否符合校验规则

True

# 结论:校验数据只校验类中定义过的字段,多传不影响,多传的数据字段直接忽略

验证少传数据是否进行校验

form_obj = views.MyForm({'username':'gary','password':'123'})
form_obj.is_valid()
False

form_obj.errors
{'email': ['This field is required.']}

总结:

也就意味着:校验数据的时候,默认情况下数据可以多传但是不能少传

forms组件渲染标签

准备工作:

def index(request):
    form_obj = MyForm()   # 将上述定义的类实例化先要生成一个空对象
    return render(request,'index.html',locals())  # 直接将该空对象传递给html页面

第一种渲染方式:

<h3>第一种渲染方式:</h3>
<br>
 <!-- 关键字:as_p -->
{{ form_obj.as_p }}    

 <!-- 关键字:as_table -->
{{ form_obj.as_table }}

 <!-- 关键字:as_ul -->
{{ form_obj.as_ul }}


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

第二种渲染方式:

<h3>第二种渲染方式:</h3>
<br>
<p>{{ form_obj.username.label }}{{ form_obj.username }}</p>
<p>{{ form_obj.password.label }}</p>
<p>{{ form_obj.email }}</p>

第二种渲染方式:可扩展性很强 但是需要书写的代码太多  一般情况下不用

第三种渲染方式:

<h3>第三种渲染方式:</h3>
{% for form in form_obj %}
    <p>{{ form.label }}:{{ form }}</p>
{% endfor %}

第三种渲染方式(推荐使用):使用for循环的方式,代码书写简单 并且扩展性也高

总结:

1、
需要在后端类名()实例化生成一个空对象

2、
forms组件只会自动帮你渲染获取用户输入的标签(input select radio checkbox)
不能帮你渲染提交按钮

3、
label属性默认展示的是类中定义的字段首字母大写的形式
也可以自己修改 直接给字段对象加label属性即可

4、
# 推荐使用第三种渲染方式渲染标签

forms组件展示提示信息

html页面

<form action="" method="post">

    {% for form in form_obj %}
        <p>
            {{ form.label }}:{{ form }}
            <span style="color: red">{{ form.errors }}</span>
        </p>
    {% endfor %}
    <input type="submit" class="btn btn-info">
</form>
def index(request):
    form_obj = MyForm()
    if request.method == 'POST':
        # 获取用户数据并校验
        # request.POST可以看成就是一个字典
        form_obj = MyForm(request.POST)
        # 判断数据是否合法
        if form_obj.is_valid():
            # 如果合法 操作数据库存储数据
            return HttpResponse('OK')
        else:
            # 不合法怎样将错误信息展示到前端页面呢
            pass
    return render(request,'index.html',locals())
 # 前端浏览器会帮我们校验数据,但是前端的校验是弱不禁风的(如果在'检查'位置修改了前端的代码块他就可以校验通过)

# 如何取消这种浏览器的校验呢
<form action="" method="post" novalidate>
# 只需要在form表单中添加novalidate属性即可

# 我们看到这个提示信息是ul>li的形式
# 因为:errors的结果是一个列表的形式,浏览器针对列表的形式会自动生成一个ul > li标签

# 解决:
<span style="color: red">{{ form.errors.0 }}</span>
# 需要在错误信息后(.)一个0
# 这样就相当于拿到索引[0]的错误信息就不会是一个列表的形式,这样浏览器就不会自动产生ul标签,只会拿到普通文本

现象:

# 我们看到在我们提交数据的时候页面刷新并没有将页面的数据清空:
# 那么这是怎么实现的呢?

# 这样数据会一直保留到数据彻底合法。

提示信息自定义:

# 现在我们的错误提示信息都是forms组件自动帮我们生成的错误信息,并且是英文的,我们就来解决这个问题:
# 参数 :error_messages={约束条件:不成立条件的提示信息}

eg:
username = forms.CharField(min_length=3,max_length=8,label='用户名',
                           error_messages={'min_length':'用户名不能少于3位',
                                           'max_length':'用户名不能大于8位',
                                           'required':'用户名不能为空'
                                          }
                          )
email = forms.EmailField(label='邮箱',
                         error_messages={
                             'invalid':'邮箱格式不正确',  # 邮箱格式需要这个参数
                             'required': '邮箱不能为空'
                         }
                        )

# 效果:

总结:

1.novalidate:取消浏览器校验数据提示信息
2.form.errors.0 :取消自动生成ul标签拿到纯文本
3.必备的条件 get请求和post传给html页面对象变量名必须一样
4.forms组件当你的数据不合法的情况下 会保存你上次的数据 让你基于之前的结果进行修改
更加的人性化
5.error_messages={参数名:参数值}   #  自定义错误提示信息

钩子函数(HOOK)

# 在特定的节点自动触发,完成相应的操作

# 钩子函数在forms组件中就类似于第二道管卡,能够让我们自定义校验规则

# 在forms组件中有两类钩子
	1.局部钩子
    	当你需要给某个字段添加校验规则的时候可以使用局部钩子
        2.全局钩子
    	当你需要给多个字段添加校验规则的时候可以使用全局钩子
        
# 书写位置:
在forms组件类里书写方法即可

示例:

# 1.校验用户名中不能含有sb
# 判断:只需要校验username字段 (局部钩子)
# 钩子函数(局部钩子)
# 关键字:clean_局部字段名
def clean_username(self):
    username = self.cleaned_data.get('username')    # 数据通过了forms组件条件的数据都会存储在cleaned_data里,所以我们拿出这里面(过了第一道关卡)的数据,进行添加第二道关卡
    
    if 'sb' in username:   # 如果数据的用户名含有'sb'
        # 提示前端展示错误信息(关键字:add_error)
        self.add_error('username','不能骂人呀~~')
    # 将钩子函数沟取出来的数据再放回去
    return username
# 2.校验密码和确认密码两次密码是否一致
# 判断:需要校验password和confirm_password两个字段  (全局钩子)
# 全局钩子
# 关键字:clean
    def clean(self):  # 定义全局钩子
        # 拿到通过forms组件的两次密码
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        if password != confirm_password:  # 如果两次密码不一致
            # 告诉前端展示错误信息
            self.add_error('confirm_password','两次密码不一致')
        # 将钩子函数够出来的数据返回
        return self.cleaned_data

整体代码:

点击查看代码
from django import forms


class MyForm(forms.Form ):
    # username字符串类型最小三位最长8位
    username = forms.CharField(min_length=3,max_length=8,label='用户名',
                               error_messages={'min_length':'用户名不能少于3位',
                                                'max_length':'用户名不能大于8位',
                                               'required':'用户名不能为空'
                                               }
                               )
    # password字符串类型最小三位,最长12位
    password = forms.CharField(min_length=3,max_length=12,label='密码',
                               error_messages={'min_length': '密码不能少于3位',
                                               'max_length': '密码不能大于8位',
                                               'required': '密码不能为空'
                                               }
                               )
    confirm_password = forms.CharField(min_length=3,max_length=12,label='确认密码',
                               error_messages={'min_length': '密码不能少于3位',
                                               'max_length': '密码不能大于8位',
                                               'required': '密码不能为空'
                                               })

    # email字符必须符合邮箱格式:xxx@xx.com
    email = forms.EmailField(label='邮箱',
                             error_messages={
                                            'invalid':'邮箱格式不正确',
                                             'required': '邮箱不能为空'
                                             }
                             )

    # 钩子函数(局部钩子)
    def clean_username(self):
        username = self.cleaned_data.get('username')
        if 'sb' in username:
            # 提示前端展示错误信息
            self.add_error('username','不能骂人呀~~')
        # 将钩子函数沟取出来的数据再放回去
        return username

    # 全局钩子
    def clean(self):
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        if password != confirm_password:
            self.add_error('confirm_password','两次密码不一致')
        # 将钩子函数狗出来的数据返回
        return self.cleaned_data

forms组件其他参数及补充知识点

forms组件字段里常见参数

label			   # 字段名
error_messages		# 自定义报错信息
initial			   # 默认值

initial

initial			   # 默认值

required

required  # 控制字段是否必填
required=False  # 字段可以为空

样式渲染

# 我们发现我们现在写的标签是没有样式的,因为标签不是我们写的,是forms组件帮助我们完成的,那么我们是不能在前端页面添加样式的。

# 那么这样我们如何给标签添加样式呢?


# 针对不同类型的input如何修改呢?
	text password redio date checkbox file
# forms组件默认渲染的都是text类型

控制input标签的type属性数据格式

# 关键字:widgef
widgef = forms.widgefs.TextInput()
widgef = forms.widgets.PasswordInput()
widgef = forms.widgets.CheckboxInput()
widgef = forms.widgets.EmailInput()
widgef = forms.widgets.FileInput()
widgef = forms.widgets.TextInput()

添加样式:

# 关键字:attrs={'属性名':'属性值'}
# 多个属性值的话 直接空格隔开即可

字段正则校验:

# 第一道关卡里面还支持正则校验


from django.core.validators import RegexValidator

phone = forms.CharField(
    validators=[
        RegexValidator(r'^[0-9]+$', '请输入数字'),  # 第一个正则约束
        RegexValidator(r'^159[0-9]+$', '数字必须以159开头')  # 第二个正则约束
    ]
)


# 只有第一个正则通过才走第二个正则

其他类型渲染:

# radio
    gender = forms.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性别",
        initial=3,   # 默认是3
        widget=forms.widgets.RadioSelect()
    )
# select
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=3,
        widget=forms.widgets.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()
    )
    
    
# 总结:
1.widgets.什么就是什么标签类型
2.只要是选择框都是 :ChoiceField
  多选框都是:MultipleChoiceField

posted @ 2022-03-08 18:55  JasonBorn  阅读(275)  评论(0编辑  收藏  举报