Django-11:Forms组件

文章介绍了Django的Forms组件在网页表单处理中的应用,包括如何创建表单类、进行数据校验、展示提示信息以及使用钩子函数进行更复杂校验。Forms组件能帮助开发者自动化渲染HTML表单、验证用户输入并提供错误提示。文章还讨论了如何自定义标签属性、取消浏览器校验以及正则表达式校验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Forms组件

一、引子

需求:

  • 书写一个具有注册功能的WEB页面,利用form表单提交数据,并在后端判断用户名和密码是否符合条件,如果不符合条件,需要将提示信息展示到前端页面上。

    1.用户名汇总不能含有“孤儿”。

    2.密码不能少于4位。

一、思路

  • 前端页面上可利用行内标签的特性,用来占位。
  • 定义一个字典,该字典的key为uname_error和passwd_error,默认value为空。
  • 后端接收前端提交的数据,并进行判断,如不符合需求,则给key添加value
  • 通过rander(,locals())以及模板语法,给span标签添加内容。

代码执行流程:

​ 1、当第一次访问时,由于span标签没有内容,所以不在前端页面做展示。

​ 2、第一次提交数据时,后端定义字典的key可能会有值。

​ 3、如果数据不符合要求,则字典对应key的value则变成报错信息。

​ 4、由于form表单提交之后,页面刷新,会再次发送GET请求。

​ 5、而此时span标签就可以展示对应的报错信息。

二、代码

  • 视图层

    def ab_forms(request):
        # 该字典默认为空,对应的,初次访问页面的时候,是没有报错提示信息的。
        back_dic = {'username':' ','password':' '}
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            if  '孤儿' in username:
                back_dic['username'] = '违规用户名'
            if len(password) < 4:
                back_dic['password'] = '密码位数最小4位'
    
        return  render(request,'ab_forms.html',locals())
    
  • 模板层

    <div class="container">
        <div class="row">
            <div class="col-lg-6">
                <h1>注册</h1>
                <form action="" method="post">
                    <p>
                        <label for="">用户名:<input type="text" class="form-control" name="username"></label>
                        <span style="color: red">{{ back_dic.username }}</span>
                    </p>
                    <p>
                        <label for="">密码:<input type="text" class="form-control" name="password"></label>
                        <span style="color: red">{{ back_dic.password }}</span>
                    </p>
                    <p><input type="submit" class="btn btn-success"></p>
                </form>
            </div>
        </div>
    </div>
    

三、效果展示

image-20211221224001651

​ 根据现象进行总结:

​ 1、再次刷新页面,提示信息仍然存在,因为dict中的value还是存在的,所以span标签仍然有数据

​ 2、提交之后,由于页面刷新,所以之前输入的数据也都没有了,体验不是很好。

1.1 forms组件

上述案例,我们自己做的事情:

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

forms组件可以帮我们做到的事情:

  • 渲染html代码
  • 校验数据
  • 展示提示信息

附:

为什么数据校验非要去后端,不能在前端利用js直接完成呢?

  • 前端的校验是弱不禁风的,可以直接修改,或者利用爬虫程序绕过前端页面直接朝后端提交数据。
  • 购物网站场景下,选取了货物之后,会计算一个价格发送给后端,如果后端不做价格的校验,那么会造成很大损失。

二、forms组件使用

步骤:

  • 视图层定义类,属性类似于模板层的字段。
  • 表单提交的数据,会和对应的“字段”规则进行校验,通过与不通过的“字段”值都会封装到对应方法中。
  • 有了错误提示信息之后,再次使用组件所提供的方法,来渲染标签。

一、视图层校验代码示例

'''
视图层
'''
from django import forms
class TestForm(forms.Form):
    username = forms.CharField(min_length=3,max_length=8)
        # username字符串类型最小3位最大8位
        
    password = forms.CharField(min_length=3,max_length=8)
		# password字符串类型最小3位最大8位
        
    email = forms.EmailField()
		# email字段必须符合邮箱格式  xxx@xx.com

二、如何校验

本小结主要介绍上面定义的TestForm类,如何发挥它的校验作用,由于是直接使用,所以并不是通过前后端交互的方式来展示效果,而是通过测试文件。

  • 测试文件除了django内部的test.py文件以外,Pycharm内部也提供的有测试环境,点击右下角的<Python console>,本小节的代码,都在这里进行测试。

基本方法:

  • 1、将待校验的数据传入

    In[2]: from app01.views import *
    In[3]: forms_obj = TestForm({'username':'ly','password':'123','email':'123@'})
    
  • 2、判断数据是否合法

    In[4]: forms_obj.is_valid()
    Out[4]: False
        
        '''
        该方法只有在所有的数据全部合法的情况下才会返回True
        '''
    
  • 3、查看所有校验通过的数据

    In[5]: forms_obj.cleaned_data
    Out[5]: {'password': '123'}
        
        '''
        实例化的时候,只有密码符合了TestForm类中的对应规则
        '''
    
  • 4、查看所有不符合校验规则,以及不符合的原因

    In[6]:forms_obj.errors
    Out[6]: 
    {'username': ['Ensure this value has at least 3 characters (it has 2).'],
     'email': ['Enter a valid email address.']}
    # 字典key的value,之所以是列表的形式,是因为报错的信息,可能不止一条,比如不满足长度的同时,还包含了特殊字符或敏感词。
    

注意:

  • 校验数据的时候,默认情况下数据可以多传但是绝不可能少传。

校验原理:

  • 待校验数据的key,与类中的字段名进行匹配,如果相同,则将对应的value与类中的字段规则做校验,如果合法就放到cleaned_data,如果不合法则放到errors。
  • 在多传值的情况下,不管是cleaded_data还是errors,都不会放进去。
  • 当类中字段没有设置可为空时,少传则会放到errors里面。

本小结介绍了,如何使用组件来校验组件是否符合要求,如何展示在前端(渲染)详情见2.1章节

复杂的校验规则(如正则)等,在后续章节中,会陆续介绍。

2.1 渲染标签的几种方式

本章节将分为三个小章节来介绍三种不同的渲染方式。

  • 三种方式各有各的好,觉得封装程度太高,留给自己二次开发的空间太小,那么就采用其他方式。

2.1.1 as_p

第一种:

  • forms组件实例化的对象中,有一个方法名叫as_p,在模板语法中使用可以渲染出表单。

    但是,没有提交按钮

'''
视图层
'''
from django import forms
class TestForm(forms.Form):
    username = forms.CharField(min_length=3, max_length=8)
    password = forms.CharField(min_length=3, max_length=8)
    email = forms.EmailField()

def my_forms(request):
    # 1.先产生一个空对象
    forms_obj = TestForm()
    # 2.直接将该空对象传递给html页面
    return  render(request,'test_forms.html',locals())
<!--模板层-->
<h1>注册</h1>
<form action="" method="post">
	{{ forms_obj.as_p }}
</form>

效果如下图:

image-20211222180003072

现象:

  • 可以看到,as_p 最终是渲染出了P标签,然后里面包裹着label标签和input标签
'''
除了as_p以外,还有as_ul  as_table 等
'''
as_ul  : li标签
as_table : input框都在一行,类似于表头。

2.1.2 .属性

第二种:

  • 组件类实例化的对象,可以通过 .属性的方式,来渲染出input标签
<h1>注册</h1>
<form action="" method="post">
	{{ forms_obj.username }}
</form>
<!--
	这里的username,为TestForm类中的,username属性。
-->

效果展示:

image-20211223112230874

现象:

  • 虽然可以渲染出标签,但是输入框的前面,没有数据了,用户看到了不知道这里应该填写什么。

解决办法:

'''
forms_obj.username可以拿到Input表单,而这个表单中又有一个名为label的属性,这个属性的值就是as_p中展示出来的值,默认格式为TestForm类中的属性名首字母大写。
'''

<form action="" method="post">
	{{ forms_obj.username.label }} {{ forms_obj.username }}
      #      提示信息                      输入框
</form>
  • 此时就会遇到一个问题,如果不想要Username,觉得在页面上不好看,想换成中文的“用户名”,该如何操作呢?

    '''
    如何修改?
    	自己设置label属性的值,不要默认的。
    '''
    # views.py
    class MyForm(forms.Form):
        username = forms.CharField(min_length=3,max_length=8,label='用户名')
    

    效果如下图:

    image-20211223115509966

2.2.3 for循环

先来看看forms_obj循环出来的是什么?

<form action="" method="post">
	{% for foo in forms_obj %}
		<p>{{ foo }}</p>
	{% endfor %}
</form>
image-20211223115659737

现象:

  • 可以看到foo在每次循环中,就等同于forms_obj.username、forms_obj.password、forms_obj.email

  • 那么我们想要获取到输入框前面的文本提示数据时,只需要foo.label即可。

    因为:
    forms_obj.xxx = foo = input框
    forms_obj.xxx.label = 提示信息

    所以:
    foo.label = forms_obj.xxx.label = 提示信息

验证效果:

<form action="" method="post">
	{% for foo in forms_obj %}
		<p>{{ foo.label }}:{{ foo }}</p>
	{% endfor %}
</form>
<img src="https://s2.loli.net/2023/02/18/iGT45oeVkn7FOL1.png" alt="image-20211223135633264" style="zoom:67%;" />

2.2 展示提示信息

在前面,介绍了如何定义“校验类”(TestForm)、一些常用的方法is_valid()、cleaned_data、errors,以及as_p等其他方式来渲染标签。

那么就差最后一步了,如何将提示信息展示在页面上,例:不符合邮箱格式、用户名不符合长度等。

本章节代码依旧和2、2.1章节一致.

from django import forms
class TestForm(forms.Form):
username = forms.CharField(min_length=3, max_length=8)
password = forms.CharField(min_length=3, max_length=8)
email = forms.EmailField()


def forms_test(request):
forms_obj = TestForm()
return render(request,'forms_test.html',locals())

模板文件,由于组件并不会渲染出提交按钮,和form表单,所以需要进行补充:

<h1>注册</h1>
<form action="" method="post" novalidate>  <!--#novalidate取消浏览器自动校验-->
{% for foo in forms_obj %}
<p>{{ foo.label }}:{{ foo }}</p>
{% endfor %}
<button type="submit" class="btn btn-success">提交</button>
</form>

渲染错误信息:

'''
2.0章节,我们知道了数据校验的格式为:TestForm({字典格式})      TestForm为自定义字段校验的类名
所以我们进行校验时代码是这样的 ↓↓↓
'''
forms_obj = TestForm()
if request.method == 'POST':
	username = request.POST.get('username')
	password = request.POST.get('password')
	email = request.POST.get('email')

	forms_obj = MyForm({'username':username,'password':password,'email':email})
    

'''
是三个表单还好,可如果表单比较多,例如:调查问卷、在线考试测评这种。
	那就会很麻烦。
'''
仔细考虑下:
	1.request.POST.get() 这个get我们还在那里见过?dict.get()?
    	对,就是字典,所以我们就可以将request.POST 看做成字典
    2.MyForm({}) 这里刚好需要的就是字典,那么不就可以  MyForm(request.POST)   
    3.如果将request.POST 当做字典进行数据校验时,校验类中的字段名,必须和前端HTML标签中表单的name属性相同。

解决办法:

  • request.POST.get()这个get还在那里见过?dict.get()就是字典,所以我们可以把request.POST这个querydict对象看成是字典对象,随后作为参数,用于实例化form_obj对象。

最终代码为:

def forms_test(request):

    # 首先 生成空对象
    forms_obj = MyForm()  

    if request.method == 'POST':
        # 校验数据
        forms_obj = TestForm(requests.POST)  
        
        # 校验结果如何合法,则操作数据库
        if forms_obj.is_valid():   
            # 输入的数据合法,保存数据库,代码略,此处为了实验方便,只是返回字符串。
            return HttpResponse('OK,注册成功')

    return  render(request,'ab_forms.html',locals())



'''
foo为每个表单的输入框
	.errors可以获取到所有错误信息的列表
	如果不取索引为0的数据,那么在列表上渲染出来的格式就为ur>li,而非span标签
	所以,为了不让它自动转换,这里可以errors.0,取索引第一位。
'''
<form action="" method="post" novalidate>  #novalidate取消浏览器自动校验
	{% for foo in forms_obj %}
		<p>
			{{ foo.label }}{{ foo }}
            
            <!--错误提示信息-->
			<span style="color: red">{{ foo.errors.0 }}</span>
		</p>
	{% endfor %}
	<input type="submit" class="btn btn-success">
</form>

展示效果:

image-20211223162019721

2.2.1 取消浏览器校验

浏览器会根据我们的HTML标签,对表单进行简单的校验,例如当“用户名”输入框的长度太短,那么浏览器将无法提交,这样我们的错误信息就看不到效果,所以需要需要浏览器自动校验

具体操作:

  • 表单后跟上novalidate属性。
<form action="" method="post" novalidate>
    
<!--
	novalidate 不校验
-->

2.2.2 提示信息自定义

在2.2章节中,实现了错误信息提示,但是却是英文的,给用户的体验不好,所以我们需要自定义提示信息

自定义提示信息:

  • 对于继承forms.Form类(如案例中的TestForm)的属性,添加error_messages属性,示例如下:

    class TestForm(forms.Form):
         username = forms.CharField(min_length=3,max_length=8,label='用户名',error_messages={
          'min_length':'用户名最少3位',
            'max_length':'用户名最多8位',
            'required':'用户名不能为空',
        })
        password = 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':'邮箱不可为空',
        })
    

    required:为空时自定义报错,如“用户名不能为空”。

    min_length:最短长度,反义为max_length。

    invalid:针对于邮箱格式问题,如:“邮箱格式不正确”。

    默认为必填,选填以及身份证校验、手机号校验等,需要利用正则校验的,会在后续章节介绍。

三、钩子函数(HOOK)

在二章节中,可以发现只是简单的校验,并没有针对表单数据去做详细的校验,这个时候就需要利用钩子函数来进行处理

在forms组件中有两类钩子:

  • 局部钩子:对单个字段的数据进行校验(如:用户名内是否含有敏感词。)
  • 全局钩子:对多个字段的数据进行校验(如:密码的二次确认。)

局部钩子:

  • 格式: clean_字段

    from django import forms
    class TestForm(forms.Form):
        username = forms.CharField(........)
    	'''
    	其他字段略
    	'''
    
        def clean_username(self):
            username = self.cleaned_data.get('username')
    		if '嘿嘿嘿' in username:
                # 展示错误信息,格式:add_error(字段,错误信息)
                self.add_error('username','携带敏感、低俗词汇')
                
            return username
    

    钩子函数书写在类的内部,如果是局部钩子那么格式就为clean_字段,例如clean_username

    钩子函数的原理就是,通过forms.CharField校验之后,form_obj对象自动执行钩子函数,随后可以获取数据,并对其进行各种校验,如正则等。

    随后通过add_error函数,给对应的字段添加错误信息,如self.add_error(‘username’,‘携带敏感、低俗词汇’)

    最后一步,将**“钩”过来的数据“返回”**。

    最后一步的return是必须的,因为分析源码内是有变量接收的,所以必须要return。

全局钩子:

  • 格式:clean

    from django import forms
    class TestForm(forms.Form):
        password = forms.CharField(........)
        confirm_password = forms.CharField(........)
    
    
        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
    

    全局钩子直接clean即可,但是钩子函数结束时,由于拿的是多个字段的数据,所以这里直接return self.cleaned_data即可。

3.1 案例1-用户名校验

案例:用户名中不可以出现’嘿嘿嘿’

  • from django import forms
    class MyForm(forms.Form):
        username = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={
            'min_length': '用户名最少3位',
            'max_length': '用户名最多8位',
            'required': '用户名不能为空',
        })
    	
        
        def clean_username(self):
            # 获取到用户名
            username_dict = self.cleaned_data.get('username')
            if '嘿嘿嘿' in username:
                # 展示错误信息,add_error(字段,错误信息)
                self.add_error('username','携带敏感、低俗词汇')
            # 将钩子函数钩去出来数据再放回去
            return username
    

    由于是做用户名校验,针对单个字段,所以选用局部钩子,函数名为clean_username

    然后cleaned_data获取对应字段的数据,格式为字典,然后有通过内置方法get(),取值。

    随后通过add_error(field,error)对指定的字段,添加错误信息。

    最后,将“钩”出来的数据,再“放”回去,所以要记得return

    附:

    在获取数据时,为什么使用的是cleaned_data而不是errors呢,因为钩子函数是在第一道校验完成之后才会进入后续校验的,所以肯定是符合前面的校验,自然数据就都在cleaned_data里。

3.2 案例2-确认密码

案例:注册页面密码的二次确认

  • 表单中有两个密码输入框,第二个作为二次确认,当两个内的数据完全一致时才可以提交。

    from django import forms
    class TestForm(forms.Form):
        '''
        基本的中文自定义报错,与基础校验代码略。
        '''
        username = forms.CharField(.....)
        password = forms.CharField(.....)
        confirm_password = forms.CharField(.....)
        email = forms.EmailField(.....)
        
    	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
    

    由于是密码字段和确认密码这两个字段做校验,所以采用全局钩子,函数名直接为clean

四、forms渲染标签属性

在上文中,提到的有label属性、error_messages属性,那么本小结将介绍其他属性,如:表单默认值、input表单的类型是text还是passwd、等等等…

label、error_messages

  • 作用:渲染表单时,修改提示性信息,不使用默认的字段名首字母大写。 后者则用于自定义报错信息。

    from django import forms
    class TestForm(forms.Form):
        username = forms.CharField(
            # input框的提示信息(字段名)
            label = '用户名',   
            
            # 自定义报错信息
            eorros_messahes = {
            	'min_length': '用户名最少3位',
            	'max_length': '用户名最多8位',
            	'required': '用户名不能为空',
            },
        )
    

initial、required

  • 作用:initial设置默认值, required用于设置是否为空,False为允许为空。

    from django import forms
    class TestForm(forms.Form):
        username = forms.CharField( 
            initial = '张三',    # 默认值,默认为“张三”
            required = False,   # False允许为空,默认为True表示不允许为空
        )
    

widget

  • 作用:修改input表单的type属性的值,默认是text,当我们需要修改为file、password时,就需要利用widget去进行修改。

    ​ 另外,当我们的表单想要添加样式的时候,也需要使用到widget

    '''
    修改type属性值
    '''
    from django import forms
    class TestForm(forms.Form):
        username = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={
                    'min_length': '用户名最少3位',
                    'max_length': '用户名最多8位',
                    'required': '用户名不能为空',
                },
                widget=forms.widgets.TextInput(),)
    
    
        password = forms.CharField(min_length=3, max_length=8, label='密码', error_messages={
                    'min_length': '密码最小3位',
                    'max_length': '密码最大8位',
                    'required': '密码不能为空',
                },
                widget=forms.widgets.PasswordInput(),)
    

    修改之后,这样password所属的表单,在输入时就不是明文的了。

    当我们需要继承比如BootStrap样式的时候,就需要使用attrs方法

    具体格式为:forms.CharField( widget = forms.widgets.类型Input(attrs{‘class’:‘类1 类2 类3’}) ),多个类直接空格隔开。

    '''
    修改样式
    '''
    class TestForm(forms.Form):
        username = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={
                    'min_length': '用户名最少3位',
                    'max_length': '用户名最多8位',
                    'required': '用户名不能为空',
                },
                                               #指定样式
                widget=forms.widgets.TextInput(attrs={'class':'form-control'}),)
    
    
        password = forms.CharField(min_length=3, max_length=8, label='密码', error_messages={
                    'min_length': '密码最小3位',
                    'max_length': '密码最大8位',
                    'required': '密码不能为空',
                },
                                                   #指定样式
                widget=forms.widgets.PasswordInput(attrs={'class':'form-control'}),)
    

五、正则校验

上文中,我们实现了对数据长度进行简单校验,以及利用钩子函数对各个表单的内容进行更加详细的判断,正则校验可以在钩子函数内导入re模块,进行判断,但是本章节将要介绍的是,如何不使用钩子函数,直接就可以完成正则校验。

格式:

  • validators=[ RegexValidator(‘正则表达式’,‘错误提示’) ,RegexValidator(),RegexValidator()… ]

    一个字段,可以通过多个正则表达式的校验,如果不通过,则显示指定的报错信息。

需求:校验手机号,并且还要是130开头的

'''
需求:校验手机号,并且还要是130开头的
'''

from django import forms

# 导入模块
from django.core.validators import RegexValidator

class MyForm(forms.Form):
    username = forms.CharField(min_length=3,max_length=8,label='用户名',)

    phone = forms.CharField(
        validators=[
            RegexValidator(r'^[0-9]+$', '请输入数字'),
            RegexValidator(r'^130[0-9]+$', '必须是130开头的11位号码')   
        ]
    )

image-20221115024721216

正则表达式尽量不要自己书写,可以在一些正则在线测试网站上,使用自动生成好的规则即可。

六、其他类型渲染

前面几个章节的代码中,永远都是forms.CharField(),如:

class MyForm(forms.Form):
username = forms.CharField(min_length=3,max_length=8,label='用户名',)
password = forms.CharField(widget=forms.widgets.PasswordInput(),)

所以本章节就是来介绍下其他标签,如radioselect等。

# 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()
    )
    
    # 多选
    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()
    )
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿煜yv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值