Django之Form组件介绍

本文介绍了Django中的Form组件,它可以帮助开发者快速处理HTML表单,包括自动生成HTML标签、验证用户数据、保留提交数据等。文章详细讲解了如何创建Form类,字段和插件的使用,以及如何进行自定义验证规则。此外,还探讨了Form组件与数据库模型字段的关系,并展示了如何通过扩展源码中的钩子函数实现复杂验证,如确认密码一致性检查。

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

--------------------------------------前言------------------------------------

在进入正题之前,想多说两句;

当我们开始学习一个新的知识的时候,比如说下面要讲的组件,我们肯定会感到陌生和困惑。这个东西是干嘛的呢?难不难学呢?学了之后又有什么用处呢?
所以呢我认为我们在学习一个新知识的时候,先需要在大体上有一个简单的认识。
比如说我们学Django,在开始之前呢,我们就了解到了它是一个web框架,它能帮助我们快速搭建web。这样的话我们就是带着目标去学的,中间就会少了很多困惑。
我想大概学习很多东西都应该是这样的一个过程

那再来谈一谈几天要说的Form组件;
在学过Django基础之后,一听到form,我们应该就会想到Form表单提交方式,
1.首先需要我们在html里面自己写form表单,里面一般有比较多的input标签,
2.标签的样式呢也需要自己选择。
3.需要标签有默认信息时,也需要自己在处理
4.提交到后台的话,有可能需要经过数据清洗,才能放到数据库。这也需要我们自己去做筛选条件

听到这里,是不是觉得就这么些个就挺麻烦的?
没错,确实是麻烦,而且这些都是比较常用的,你可能需要根据情况的不同,重复的去写这些东西,这样下去,是不是觉得web开发很繁杂呢?

这个时候就要介绍Django自带的Form组件了。所谓的组件呢,其实和框架什么的一样,都是帮助我们集成了一些常用的功能,提供接口给我们,直接用就行了。

-------------------------------------正文----------------------------------------------

那Form组件有哪些功能呢?没错!上面提到的它里面都有!

  • 生成HTML标签
  • 验证用户数据(显示错误信息)
  • HTML Form提交保留上次提交数据
  • 初始化页面显示内容

下面来看看我们怎么用它吧:
用models.py先在数据库生成一张表:

class EgForm(models.Model):
    user = models.CharField(max_length=32)
    gender = models.BooleanField()
    city = models.CharField(max_length=12)
    pwd = models.CharField(max_length=20)
    email=models.EmailField()

然后要创建一个Form类(建议也放在models.py里):

from django.forms import Form
from django.forms import widgets
from django.forms import fields
class MyForm(Form):
    user = fields.CharField(
        widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'})
    )
    gender = fields.ChoiceField(
        choices=((1, '男'), (2, '女'),),
        initial=2,
        widget=widgets.RadioSelect
    )
    city = fields.CharField(
        initial=2,
        widget=widgets.Select(choices=((1, '上海'), (2, '北京'),))
    )
    pwd = fields.CharField(
        widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
    )
    email=fields.EmailField()

类属性最好和数据字段名相同,后面会讲到为什么

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML。
如上面的CharField()EmailField()就叫做字段

还可以通过widget=widgets.RadioSelect来选择增加插件

字段和插件帮我们在内部封装了一些筛选条件在字段属性里面,主要是通过正则来实现的

eg:
Field字段属性

required=True,               是否允许为空
widget=None,                 HTML插件
label=None,                  用于生成Label标签或显示内容
initial=None,                初始值
help_text='',                帮助信息(在标签旁边显示)
error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[],               自定义验证规则
localize=False,              是否支持本地化
disabled=False,              是否可以编辑
label_suffix=None            Label内容后缀

CharField(Field)字段特有属性(因为继承了Field,所以上面的属性它也都有)

max_length=None,             最大长度
min_length=None,             最小长度
strip=True                   是否移除用户输入空白

views函数

内有较详细注释

from django.shortcuts import render, redirect,HttpResponse

def index(request):
    if request.method == "GET":
        obj = MyForm()
        return render(request, 'index.html', {'form': obj})
        #示例化form类,并传给html
    elif request.method == "POST":
        obj = MyForm(request.POST, request.FILES)
        if obj.is_valid():          #是否通过验证,通过是Ture,否则是False
            # values = obj.clean()
            # EgForm.objects.create(user=values["ueser"],gender=values["gender"],
            #                       city=values["city"],pwd=values["pwd"],email=values["email"])
            EgForm.objects.create(**obj.clean()) #form类属性和表字段名对应的好处
            #EgForm.objects.create(**obj.cleaned_data)
            return HttpResponse("数据添加成功")
        else:
            errors = obj.errors
        return render(request, 'index.html', {'form': obj})
        # HTMLForm提交保留上次提交数据,并呈现错误信息
    else:
        return redirect('http://www.google.com')

Html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/index/" method="POST" enctype="multipart/form-data" novalidate>
    {% csrf_token %}
    <p>用户名{{ form.user }} {{ form.user.errors }}</p>
    <p>性别{{ form.gender }} {{ form.gender.errors }}</p>
    <p>城市{{ form.city }} {{ form.city.errors }}</p>
    <p>密码{{ form.pwd }} {{ form.pwd.errors }}</p>
    <p>邮箱{{ form.email }} {{ form.email.errors }}</p>
    <input type="submit"/>
</form>
</body>
</html>

注:上面的html文件是一种简介方式,换成一般的input标签,在标签名和form类字段一一对应后用form类来接收数据,一样能达到验证效果

效果展示:
在这里插入图片描述

格式错误时 的效果:
在这里插入图片描述

下面自己动手试试吧

补充一点:

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 获取的值无法实时更新,那么需要自定义构造方法从而达到此目的。

以上面建的表为例写Form类:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
 
class MyForm(Form):
 
    city = fields.IntegerField(
    	#这里要用IntegerField,用Charfieldhui会报“Select a valid choice. 3 is not one of the available choices.”错误,亲测,望朋友们别再踩坑......
        # choices=((1, '上海'), (2, '北京'),)  #自己写
        initial=2,
        widget=widgets.Select
    )
 
    def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        # self.fields['city'].widget.choices = ((1, '上海'), (2, '北京'),)
        # 或
        self.fields['city'].widget.choices = models.Classes.objects.all().values_list('id',"city")
        #从数据库实时获取

再再补充一点:

我们已经知道form组件中的字段有验证功能,但这些字段是不是似曾相识?
没错,我们在models.py中创建表时也有这些字段,比如Emailfield,
那你可能回想了,这个不是原本就有验证功能吗? 还要自己再去搞Form组件干嘛呢

是的,我们建表时确实有这么些个字段,但表是存在于数据库中的,数据库表结构有这些字段?

答案是没有。就算是models.py里建表写的是Emailfield,数据填入表中都是字符串,不是说不是email格式就不能填入了。

这里提一下,models里面的字段和数据库字段都有一个对应关系。详见Django之model操作:一,字段:2,中的自定义无符号整数字段

这里可能又会想了:那我们特地写成Emailfield干嘛,都写Charfield不就好了。确实实际上也没错,但Emailfield到底用来干嘛的呢?

结果就是这些特别字段是给 Django自带的admin做筛选用的 ,知道这一点就可以了,预知详情,查看admin相关吧

出了这些之外,还记不记得有一些属性,比如unique,null等等
这些属性在数据库表结构中也有,所以这些属性是通用的
比如你在model,py建表时给一个字段设置了unique=Ture,那么你往表中添加一个已存在的信息,就会报错

这一点我们可以用异常处理拿到错误信息,作为Form组件的一个补充,

当然Form组件也可以自定义扩展,后续再讲吧

Form组件自定义验证规则:

方式一:利用field字段属性 validators=[],

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
    user = fields.CharField(
    	error_messages={'invalid': '...'}
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字',), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
    )

这个属性是一个列表,可以同时包含多个验证规则,前面是正则表达式,后面是报错信息

方式二:利用RegexField(CharField)字段
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={‘invalid’: ‘…’}

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
	user=fields.RegexField(regex=r'^[0-9]+$',error_messages={'invalid': '请输入数字'})
    )

Form组件扩展验证规则

上面说过的都是都是对单个字段的验证,那我们有一个经常遇到的问题就是注册时的密码和确认密码要保持一致怎么验证呢?
这里要对源码里的钩子函数做扩展:

form类:

from django.forms import Form
from django.forms import fields
from django.core.exceptions import ValidationError
class RegisterForm(Form):
    username=fields.CharField()
    password=fields.CharField()
    confirm_pwd=fields.CharField()
    
    def clean(self):
        v1=self.cleaned_data["password"]
        v2=self.cleaned_data["confirm_pwd"]
        if v1==v2:
             '''数据没问题,那么原封不动返回即可'''
            return self.cleaned_data 
        else:
        # 错误信息储存到 errors {'__all__':[e,]}
            raise ValidationError("密码输入不一致")

对内部验证过程进行一下梳理:
有数据传到后台实例化form类之后,会把数据一条一条的对应验证规则进行验证,但是没有验证顺序,哪一个字段先验证都是随机的。一条验证成功后,会把数据放到clean_data的对应字段里,如果不成功,会raise ValidationError ,并把对应错误放到errors的对应字段。全部成功,is_valid=Ture,否则为False

Django的form组件在源码中给用户留了一个扩展点,也叫勾子函数:
看下源码里的一段:
在这里插入图片描述

源码简析:这里有一个异常处理和反射,hasattr判断有没有clean函数,如果有就执行getattr这个函数,后面加了(),代表执行,返回值赋给value。验证通过的话把clean_data返回就行,不通过报ValidationError异常(因为源码只捕捉这个异常,所以只能报这个异常),然后会把错误信息加到errors._all_

对应数据处理函数:

def regiter(request):
    v=RegisterForm("request.POST")
    if v.is_valid():
        pass
    else:
        # v.errors={
        #     _all_:[],
        #     username:[],
        #     password:[],
        #     confirm_pwd:[]
        # }
        # v.errors["username"]   拿到username单个字段的错误信息
        # v.errors["_all_"]      #拿到共同的错误信息,比如不一致的错误信息就放在这
        # 又因为源码里面NON_FIELD_ERRORS="_all",所以v.errors[NON_FIELD_ERRORS]=v.errors["_all_"]
        return render(request,"register.html",{"v":v})

    '''
    但在模板语言里。不能通过{{v.errors.NON_FIELD_ERRORS.}}和{{v.errors._all_}}
    来取到错误信息,会报错,
    正确的是{{v.non_field_errors}}
    '''

以上是form组件扩展,但是我们已经知道form组件是帮我们封装功能的,一些额外需要的功能可以根据源码提供的扩展点来扩展,当然也可以自己在后端数据处理函数那里来写;
比如两次输入一致问题,单个字段用form组件验证,验证通过后,我们自己再拿到两条信息做判断,不一致的话我们在手动加一个错误信息到obj.errors,并做相应处理,也一样可以完成功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值