前言
1、在写表单的时候,会发现表单中的Field和模型中的Field基本上是一模一样的。而且一般情况下表单中需要验证的数据就是我们模型中需要保存的数据。那么这个时候我们就可以将模型中的字段和表单中的字段进行绑定
2、比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应该怎么写呢?
⑴前端:首先会在前端一个一个罗列出这些字段,让用户去填写,然后后台一个一个接收用户的输入
⑵后台:定义一个学生模型,用来保存学生信息
⑶后台:定义一个学生表单,用来验证前端传递过来的数据
⑷后台:在视图函数中使用get()方法来一个一个的获取已通过验证的数据,然后使用模型中的QuerySet方法将数据保存起来
3、在上面示例中:其实表单的定义和模型的定义其实是差不多的,但是如果按照上面这种方式来的话,一个差不多的东西我们就需要完整的定义两边,这样就显得混麻烦了
4、因此Django就提供了ModelForm组件:这个组件主要就是用来整合表单和模型,将它们两个连接起来使用。就不需要完整的定义两次了
例1:
⑴编辑视图
⑵官方文档:https://docs.djangoproject.com/en/dev/topics/forms/modelforms/
ModelForm组件
1、ModelForm顾名思义就Form和Django的Model数据库模型结合体,可以简单、方便得对数据库进行增加、编辑操作和验证标签的生成
2、Django中Model负责操作数据库,并且具有简单的数据库验证功能(基本不用);Form用于用户请求的验证,具有强悍的数据库验证功能;ModelForm是将二者合二为一,即可用于数据库操作(部分),也可用于用户请求的验证(部分)。但由于ModelForm的耦合性太强,其作用一般用作于结构简单的小站点
3、Form组件和ModelForm的区别
⑴ModelForm是Django Model.py和Form组件的结合体,可以简单、快速使用 Form验证和数据库操作功能,但不如Form组件灵活
⑵如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操),建议优先使用ModelForm,用起来更方便些
⑶但是在使用ModelForm的时候慎用fields='__all__',获取数据库所有字段势必造成性能损耗
4、适用场景:
⑴ModelForm:中小型应用程序。因为ModelForm是依赖于models的。
⑵Form:大型应用程序
ModelForm所有属性
class Meta:
model, # 对应Model的
fields=None, # 字段
exclude=None, # 排除字段
labels=None, # 提示信息
help_texts=None, # 帮助提示信息
widgets=None, # 自定义插件
error_messages=None, # 自定义错误信息
field_classes=None # 自定义字段类 (也可以自定义字段)
localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
全验证
全验证的意思就是:模型中的字段全部需要在表单中进行验证,并且验证后的所有字段都需要进行数据库操作
例2:
⑴定义模型
⑵定义表单
⑶编辑视图
⑷访问:正确
⑸访问:错误
⑹访问:错误
注:
1、model = models.Article:告诉Django这个form类和哪个model类对应
2、fields = "__all__":告诉Django这个form类里面有哪些字段(需要验证)
⑴这里的"__all__"就表示:Article模型里面的所有字段都需要表单验证
⑵可以看到,这里的表单中我们就没有定义那些需要验证的字段了,而是直接引用了模型中的字段,这样就少定义了一次
3、因为我们表单中没有定义字段了,因此如果要使用Django自带的Validator时,可以直接在模型字段的参数中添加的(这样跟在表单中是一样的)
⑴在模型中写Validator的写法与在表单中的写法是一样的
4、这里需要注意下模型字段名、表单字段名、前端input标签的name属性值三者之间的关系
⑴首先:在视图函数中获取前端input标签中的值:是通过input标签的name属性值来获取的
⑵因此:在视图函数中使用request.POST.get("键名")来获取具体值时,"键名要与"input标签的name属性值一致
⑶如果使用的是表单、模型分开模式:那么表单中的字段就需要与input标签的name属性值一致。这样在进行验证时,每个字段才能一一对应上(模型的字段名可以不与表单字段名一致)
⑷如果使用的是ModelForm模式:因此表单直接使用的是模型中的字段名,因此模型中的字段名要与input标签的name属性值一致
部分验证
1、部分验证:即模型中的字段只有其中一部分需要在表单中验证
2、部分验证的实现方法有两种:
⑴fields = ["需要验证的字段"]:表示只有列表中的字段才会验证
⑵exclude:表示不需要验证的字段(从表单中排除的字段列表)
方法一
1、使用fields = ["需要验证的字段"]:表示只有列表中的字段才会验证
例3:
⑴编辑模型
⑵编辑表单
⑶访问
方法二
1、使用exclude属性:将不需要验证的模型字段排除
例4:
⑴编辑表单
⑵访问:正确
⑶访问:错误
自定义错误信息
1、从前面的例子中可以发现:在验证模型字段时,如果没有整理错误信息的话,那么返回的错误信息就是英文的
2、因此更多的时候,需要我们自己定义下错误信息
3、在表单类中可以使用"error_messages"属性来定义错误信息,其值是一个字典型数据
例5:
⑴编辑表单
⑵访问
注:格式
error_messages = {
"验证字段名1": {
"错误信息Key1": "错误信息1",
"错误信息Key2": "错误信息2"
},
"验证字段名2": {
"错误信息Key1": "错误信息1",
"错误信息Key2": "错误信息2"
}
}
添加验证器
1、添加验证器,可以添加Django自带的验证器也可以添加自定义的验证器
⑴因为我们表单中没有定义字段了,因此如果要使用Django自带的Validator时,可以直接在模型字段的参数中添加的(这样跟在表单中是一样的)
2、在ModelForm中添加自定义验证器:跟在Form类中定义验证器是一样的
例6:
⑴编辑表单
⑵访问:正确
⑶访问:错误
save()方法
1、ModelForm类还有save()方法:可以在验证完成后直接调用save()方法,就可以将这个数据保存到数据库中
2、save()方法必须要在clean没有问题后才能使用,如果在clean之前使用,那么就会抛出异常
3、在调用save()方法的时候,如果传入了一个commit=False参数,那么只会生成这个模型对象,而不会把这个对象真正的插入到数据库中
⑴比如表单上验证的字段没有包含模型中的所有字段,这个时候就可以先创建对象,再填充其他字段,把所有字段的值都补充完全后,再保存到数据库中
例7:
⑴编辑表单
⑵编辑视图
⑶访问
注:
1、直接使用save()方法的前提是:模型中的所有字段都在表单中进行验证了
⑴这个例子中的"create_time"比较特殊:它是自动生成的,不需要前端传入。虽然是__all__其实实际上也不会验证它
⑵由前端传入且需要入库的字段:若没有在表单中进行验证那么就不能直接使用save()方法来保存了。需要在视图函数中单独获取
2、有种情况是:模型中的字段只有一部分在表单中验证了。这个时候就不能直接使用save()方法了
例8:注册实例
⑴编辑模型
⑵编辑表单
⑶编辑视图
⑷补充
⑸补充访问
注:
1、不管是使用表单、模型分离还是使用ModelForm,表单中定义的字段必须与模板表单字段完全一致(字段名、个数)
2、比如这个例子中:password2在模型中没有定义(不需要入库),但是在模板中定义了这个字段。所以不管表单使用的是ModelForm还是表单、模型分离(这里使用的是ModelForm),都必须在表单中单独定义一个password2。表单中定义的字段必须与模板表单字段完全一致(字段名、个数)
拓展
更新数据
1、因为前面的例子中都是增加数据的,没有更新数据的,所有自己试了下用于更新数据的
例9:
⑴编辑模型
⑵编辑表单
⑶编辑视图
⑷访问:增加数据
⑸访问:更新数据
注:
1、其实这里在更新数据时还是要通过get()方法从表单中一个一个的获取数据,跟前面直接使用Form表单时差不多的
2、个人感觉:使用Form表单比使用ModelForm表单要方便点吧
⑴Form表单:是将表单和模型分开来的,两个互不影响,所以感觉要简单灵活点
⑵ModelForm表单:需要同时考虑表单和模型
使用原生SQL与表单
自己觉得表单与模型分开起来比较好:虽然需要在视图中单独获取一次数据,但是操作起来比较灵活。另外还有一种就是不使用ORM模型,直接使用SQL语句来操作数据库(这里结合表单:感觉表单这个比较好,将数据验证逻辑单独放在一个PY文件中,不需要写在视图函数中)
例10:获取图书
数据源
⑴逻辑处理函数:因此直接从数据库中查询出来的数据可能与预期的不一致,所以单独编写了一个类来出来查询出来的数据和列名(单独写在一个地方:在视图函数中调用就可以了)
class DealData():
def __init__(self):
pass
def DealSelectData(self, selectDatas):
"""
处理查询出来的数据
"""
# 定义一个大列表来装所有子数据列表:是一个嵌套了列表的列表
selectDataLists = []
for selectData in selectDatas:
# 定义一个小列表来装该条数据
selectDataList = []
for eachData in selectData:
# 如果字段值为整形、浮点型、字符串:那么不需要整理,直接加入到该条数据列表中
if isinstance(
eachData,
int) or isinstance(
eachData,
str) or isinstance(
eachData,
float):
selectDataList.append(eachData)
# 如果字段值为datetime类型:那么就将其转为字符串型的时间值
elif isinstance(eachData, datetime.datetime):
eachData = eachData.strftime('%Y-%m-%d')
selectDataList.append(eachData)
# 如果字段值为None:那么就添加null,毕竟数据库中空值表示为null
elif eachData is None:
selectDataList.append("null")
else:
print("该类型的值未整理:请添加相应代码")
selectDataLists.append(selectDataList)
# print("整理后的查询数据为:", selectDataLists)
return selectDataLists
def DealColName(self, colNames):
"""
处理查询结果对应的列名
"""
colNamesList = []
for colName in colNames:
colName = colName[0].lower() # 同时将列名全部转为小写
colNamesList.append(colName)
print("整理后的列名为:", colNamesList)
return colNamesList
def CreateDict(self, colNamesList, selectDataLists):
"""将查询结果和类名整理成字典"""
selectDataDictList = [dict(zip(colNamesList, selectDataList))
for selectDataList in selectDataLists]
print("最终整理后的查询结果为:", selectDataDictList)
return selectDataDictList
⑵编辑视图
⑶编写模板
⑷访问
例11:提交数据
⑴创建一个数据库表:不用ORM模型
⑵定义表单
⑶编辑视图
⑷编辑模板
前后端分离+模型、表单分离
1、前后端分离:前端模板单独编写,只使用Django表单组件的验证功能(不使用其渲染功能)
2、模板、表单分离:不使用ModelForm,将表单和模型分开定义
⑴我感觉将表单和模型分开比较好点:表单提交上来的数据即使通过了验证,可能也需要我们进一步处理后才会存到数据库中去,而不是说获取到了什么就直接存到数据库
⑵所以在视图函数中再次获取表单数据能更方便我们处理数据
⑶而,如果直接使用ModelForm的话,我们就不能处理数据了
例12:
⑴编辑模型
⑵编辑表单
⑶编辑视图
⑷编辑模板
注:
1、表单字段名、模型字段名、模板input标签名:这三个数据最好保证一样,避免弄混了
2、表单字段名和模板input标签名必须一样,不然在验证时会获取不到对应的数据值