【12.0】Django框架之form组件
【一】需求
- 写一个注册功能
- 获取用户名和密码,利用form表单提交数据
- 在后端判断用户名和密码是否符合一定的条件
- 用户名中不能包含啦啦啦
- 密码不能少于三位
- 如果符合条件需要你将提示信息展示到前端页面
【二】form表单实现
【1.0】点击提交按钮返回比对信息
- 前端页面
<form action="" method="post">
<p>username:
<input type="text" class="form-control" name="username">
<span style="color: red">{{ back_dict.username }}</span>
</p>
<p>password:
<input type="password" class="form-control" name="password">
<span style="color: red">{{ back_dict.password }}</span>
</p>
<input type="submit" class="btn btn-danger">
</form>
- 后端
from django.shortcuts import render
# Create your views here.
def ab_form(request):
back_dict = {}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if "啦啦啦" in username:
back_dict['username'] = '用户名不符合格式!'
if len(password) < 3:
back_dict['password'] = '输入的密码长度不够!'
'''
无论是 get 请求 还是 post 请求,页面都能获取到字典
get 请求时,字典是空的,没有值
post 请求时 ,字典可能是非空的
'''
return render(request, 'ab_form.html',locals())
- 路由
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# form 组件引入
re_path(r'^ab_form/',views.ab_form),
]
- 用到的技术点
- 手动书写前端HTML页面代码
- 渲染HTML页面
- 后端对用户数据进行校验
- 校验数据
- 对不符合要求的数据进行前端提示
- 展示提示信息
- 手动书写前端HTML页面代码
【2.0】form组件
-
form组件可以帮我们完成
- 渲染HTML页面
- 校验数据
- 展示提示信息
-
为什么数据校验要在后端执行,而不是前端在JS完成?
- 数据校验前端可有可无
- 但是后端必须有!
-
原因
- 前端的校验存在实时被修改的风险,可以在前端直接修改输入的数据
- 利用爬虫程序可以绕过前端页面直接向后端提交数据
-
例如购物网站
- 在前端计算完总价后,直接提交给后端
- 后端如果不做校验就会产生极大的风险安全问题
- 正确的做法是
- 在后端查出所有商品的必要信息,再次计算一遍
- 前段与后端都要进行校验,保证数据的安全性
【三】forms组件如何校验数据
【1】基本使用
# 导入模块
from django import forms
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3)
# # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3)
# email : 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField()
【2】校验数据
- 测试环境可以拷贝代码准备
- 其实在pycharm已经为我们提供了测试环境
- 底下的
Python Console
- 底下的
(准备数据)
# 导入模块
from app01 import views
# 将想要传入的数据组织成字典的格式传入即可
form_obj = views.MyForm({"username":"dream","password":"521","eemail":"123"})
(1)判断传入的数据是否合法
form_obj.is_valid()
# False
- 该方法,只有所有传入的数据都合法的情况下才会返回True
(2)查看那些数据是合法的
form_obj.changed_data
# ['username', 'password']
- 展示符合校验规则的数据
(3)查看不合法的数据格式
form_obj.errors
# {'username': ['Ensure this value has at most 2 characters (it has 5).'], 'password': ['Ensure this value has at most 2 characters (it has 3).'], 'email': ['This field is required.']}
- 查看所有不符合规则的参数及不符合的原因
- 报错原因可能有多个
(4)多传参数是否会报错
form_obj = views.MyForm({"username":"dream","password":"521","eemail":"123@qq.com","hobby":"music"})
form_obj.is_valid()
# True
- 校验数据只校验类中出现的字段
- 如果有多传的字段会直接自动忽略
(4)少传参数是否会报错
form_obj = views.MyForm({"username":"dream","password":"521"})
form_obj.errors
# {'email': ['This field is required.']}
- 校验数据
- 默认情况下,类中所有的字段都必须传值
(5)小结
- 默认情况下
- 校验数据可以多传
- 但是不能少传
【四】forms组件渲染HTML代码
- forms组件只会帮我们渲染用户输入的标签(input/select...)
【1】渲染方式一
- 路由
# forms 组件渲染html标签
re_path(r'^index/',views.index),
- 后端代码
from django import forms
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3)
# # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3)
# email : 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField()
def index(request):
# (1)产生一个空对象
form_obj = MyForm()
# (2) 直接将空对象传递给前端页面
return render(request,'index.html',locals())
- 前端页面
<form action="" method="post">
<p>第一种渲染方式</p>
{{ form_obj }}
</form>
- 渲染出的效果是
<form action="" method="post">
<p>第一种渲染方式</p>
<label for="id_username">Username:</label><input type="text" name="username" maxlength="8" minlength="3"
required="" id="id_username">
<label for="id_password">Password:</label><input type="text" name="password" maxlength="8" minlength="3"
required="" id="id_password">
<label for="id_email">Email:</label><input type="email" name="email" required="" id="id_email">
</form>
特点是
- 代码书写极少,封装程度太高,不便于后期拓展
- 一般情况下用于本地测试使用
【2】渲染方式二
<form action="" method="post">
<p>第二种渲染方式</p>
{{ form_obj.as_ul }}
</form>
- 渲染到页面的效果是
<form action="" method="post">
<p>第二种渲染方式</p>
<li><label for="id_username">Username:</label> <input type="text" name="username" maxlength="8" minlength="3"
required="" id="id_username"></li>
<li><label for="id_password">Password:</label> <input type="text" name="password" maxlength="8" minlength="3"
required="" id="id_password"></li>
<li><label for="id_email">Email:</label> <input type="email" name="email" required="" id="id_email"></li>
</form>
- 该方法可以修改后端传入的属性值
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3, label="用户名")
# # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3)
# email : 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField()
def index(request):
# (1)产生一个空对象
form_obj = MyForm()
# (2) 直接将空对象传递给前端页面
return render(request, 'index.html', locals())
- 前端
<form action="" method="post">
<p>第二种渲染方式</p>
{# 对象点属性取到对应的属性值 #}
{{ form_obj.username.label }} {{ form_obj.username }}
</form>
- 前端页面展示效果
<form action="" method="post">
<p>第二种渲染方式</p>
用户名 <input type="text" name="username" maxlength="8" minlength="3" required="" id="id_username">
</form>
- 特点
- 可扩展性非常强
- 但是需要书写复杂的代码
- 一般情况下不用
【3】渲染方式三(推荐使用)
- 前端
<form action="" method="post">
<p>推介使用第三种</p>
{% for form in form_obj %}
<p> {{ form.label }} : {{ form }}</p>
{% endfor %}
</form>
- 页面展示
<form action="" method="post">
<p>推介使用第三种</p>
<p> 用户名 : <input type="text" name="username" maxlength="8" minlength="3" required="" id="id_username"></p>
<p> Password : <input type="text" name="password" maxlength="8" minlength="3" required="" id="id_password"></p>
<p> Email : <input type="email" name="email" required="" id="id_email"></p>
</form>
- 优点
- 代码书写简单,扩展性很高
【4】总结
-
先在后端生成空对象
-
再在前端拿到想要的属性
-
label属性默认展示的是类中定义的字段首字母大写的形式
-
支持自己修改字段注释
- 直接在字典后面加 label 属性赋值即可
username = forms.CharField(max_length=8, min_length=3, label="用户名")
-
不会渲染提交按钮
【五】forms组件渲染错误信息
【1.0】LowBe版
- 后端
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3, label="用户名")
# # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3, label="密码")
# email : 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField(label="邮箱")
def index(request):
# (1)产生一个空对象
form_obj = MyForm()
# (2) 直接将空对象传递给前端页面
# (3) 获取前端输入的信息,校验并返回结果
if request.method == "POST":
# 获取前端输入的信息
'''
数据获取繁琐
校验数据需要构造成字典格式传回
ps: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">
<p>推介使用第三种</p>
{% for form in form_obj %}
<p> {{ form.label }} : {{ form }}</p>
<span style="color: red">{{ form_obj.errors }}</span>
{% endfor %}
<input type="submit" class="btn btn-danger">
</form>
- 前端展示
<form action="" method="post">
<p>推介使用第三种</p>
<p> 用户名 : <input type="text" name="username" maxlength="8" minlength="3" required="" id="id_username"></p>
<span style="color: red"></span>
<p> 密码 : <input type="text" name="password" maxlength="8" minlength="3" required="" id="id_password"></p>
<span style="color: red"></span>
<p> 邮箱 : <input type="email" name="email" required="" id="id_email"></p>
<span style="color: red"></span>
<input type="submit" class="btn btn-danger">
</form>
【2.0】升级版
- 浏览器自动帮我们做数据校验,但是前端的校验不安全
- 如何禁止浏览器做校验?
- 在form表单中加入参数即可
<form action="" method="post" novalidate>
- 前端
<form action="" method="post" novalidate>
<p>推介使用第三种</p>
{% for form in form_obj %}
<p> {{ form.label }} : {{ form }}</p>
<span style="color: red">{{ form_obj.errors.0 }}</span>
{% endfor %}
<input type="submit" class="btn btn-danger">
</form>
- 页面展示
<form action="" method="post" novalidate="">
<p>推介使用第三种</p>
<p> 用户名 : <input type="text" name="username" maxlength="8" minlength="3" required="" id="id_username"></p>
<span style="color: red"></span>
<p> 密码 : <input type="text" name="password" maxlength="8" minlength="3" required="" id="id_password"></p>
<span style="color: red"></span>
<p> 邮箱 : <input type="email" name="email" required="" id="id_email"></p>
<span style="color: red"></span>
<input type="submit" class="btn btn-danger">
</form>
- 上面的页面提示信息是英文的,如何展示中文?
- 添加字段信息
# 定义form类 class MyForm(forms.Form): # username : 字符串类型 最小三位,最大八位 username = forms.CharField(max_length=8, min_length=3, label="用户名", error_messages={ "max_length": "最大八位", "min_length": "最小三位", "required": "必填字段", }) # # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位 password = forms.CharField(max_length=8, min_length=3, label="密码", error_messages={ "max_length": "最大八位", "min_length": "最小三位", "required": "必填字段", }) # email : 必须符合邮箱格式 xxx@xx.com email = forms.EmailField(label="邮箱", error_messages={ "invalid": "格式不正确", "required": "必填字段", })
【注意事项】
-
必要条件
- get 请求 和 post 传给页面对象变量名必须一样
def index(request): # (1)产生一个空对象 form_obj = MyForm() # ******这里的变量名 # (2) 直接将空对象传递给前端页面 # (3) 获取前端输入的信息,校验并返回结果 if request.method == "POST": # 获取前端输入的信息 ''' 数据获取繁琐 校验数据需要构造成字典格式传回 ps:request.POST 可以看成是字典数据的格式 ''' # 校验并返回结果 form_obj = MyForm(request.POST) # ******这里的变量名 # 判断数据是否合法 if form_obj.is_valid(): # 合法操作数据库存储数据 return HttpResponse("OK") else: # 数据不合法 -- 展示错误信息给前端 pass return render(request, 'index.html', locals())
-
forms 组件
- 当你的数据不合法的情况下,会记录上一次的数据,基于上一次的数据再编辑
【六】钩子函数(Hook)
【1】什么是钩子函数
- 在forms组件中
- 钩子函数(Hooks)是用来在特定事件发生时执行自定义逻辑的函数。
- 它们提供了一种创建交互性和动态行为的方式,并可以用于处理表单的各种状态和数据。
下面是一些常见的forms组件中使用的钩子函数:
-
onInputChange: 当输入框的值发生变化时触发。你可以通过这个钩子函数获取最新的输入值,并进行相应的处理。
-
onSubmit: 当表单提交时触发。你可以在这个钩子函数中获取表单中的所有字段值,并进行数据验证、提交或其他操作。
-
onBlur: 当输入框失去焦点时触发。你可以在这个钩子函数中执行验证操作,例如检查输入是否符合预期的格式或是否满足某些条件。
-
onFocus: 当输入框获得焦点时触发。你可以在这个钩子函数中执行一些针对输入框焦点状态的逻辑操作,例如显示一个下拉列表或提示信息。
-
onReset: 当表单重置时触发。你可以在这个钩子函数中对表单进行一些初始化操作,将表单恢复到初始状态。
- 除了上述常见的钩子函数外,不同的forms组件可能还有其它特定的钩子函数,用于处理更具体的需求。
- 在使用特定的forms组件之前,建议查阅相应的文档或官方手册,以了解可用的钩子函数及其使用方式。
- 在特定的节点自动触发完成响应动作
- 钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则
- 在forms组件中有两类钩子
- 局部钩子
- 当需要给某个字段增加校验规则的时候使用
- 在自定义的 forms 类中添加类方法即可
- 全局钩子
- 当需要给多个字段增加校验规则的时候使用
- 在自定义的 forms 类中添加类方法即可
【2】案例
(1)校验用户名中不能含有 666
只需要校验 username 字段 --- 局部钩子
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3, label="用户名",
error_messages={
"max_length": "最大八位",
"min_length": "最小三位",
"required": "必填字段",
})
# # password : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3, label="密码",
error_messages={
"max_length": "最大八位",
"min_length": "最小三位",
"required": "必填字段",
})
# confirm_password : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
confirm_password = forms.CharField(max_length=8, min_length=3, label="确认密码",
error_messages={
"max_length": "最大八位",
"min_length": "最小三位",
"required": "必填字段",
})
# email : 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField(label="邮箱",
error_messages={
"invalid": "格式不正确",
"required": "必填字段",
})
# 钩子函数
# 局部钩子
def clean_username(self):
# 获取到用户名
username = self.cleaned_data.get("username")
if "666" in username:
# 提示给前端错误信息
self.add_error("username", "用户名不能包含敏感词")
# 将钩子勾出来的数据再放回到原来的逻辑中
return username
(2)校验密码和确认密码是否一致
需要校验 password 和 confirm_password 两个字段 --- 全局钩子
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3, label="用户名",
error_messages={
"max_length": "最大八位",
"min_length": "最小三位",
"required": "必填字段",
})
# # password : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3, label="密码",
error_messages={
"max_length": "最大八位",
"min_length": "最小三位",
"required": "必填字段",
})
# confirm_password : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
confirm_password = forms.CharField(max_length=8, min_length=3, label="确认密码",
error_messages={
"max_length": "最大八位",
"min_length": "最小三位",
"required": "必填字段",
})
# email : 必须符合邮箱格式 xxx@xx.com
email = forms.EmailField(label="邮箱",
error_messages={
"invalid": "格式不正确",
"required": "必填字段",
})
# 钩子函数
# 局部钩子
def clean_username(self):
# 获取到用户名
username = self.cleaned_data.get("username")
if "666" 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组件其他参数及补充
【1】基本参数
【1】autocomplete
:
设置是否启用自动填充功能,可以使用以下值:
on
:浏览器自动填充表单项。off
:禁用浏览器自动填充。
【2】disabled
:
设置表单项是否禁用,禁用后用户无法编辑或选择该项的值。
【3】readonly
:
设置表单项是否只读,只读的表单项可以显示但不允许用户修改。
【4】max
和 min
:
用于限制数字或日期类型的输入值的最大和最小值。
【5】***maxlength
和 minlength
:
设置输入值的最大长度和最小长度,超过或低于长度限制时将触发验证错误。
【6】pattern
:
使用正则表达式指定输入值的格式验证规则。
【7】required
:
设置表单项是否为必填项,如果未填写必填项,提交表单时将触发验证错误。
【8】step
:
用于数字类型的输入值,指定允许的增量或减量范围。
【9】aria-describedby
:
指定与表单项关联的描述性文本的id,辅助技术可以使用此信息提供更好的可访问性。
【10】aria-invalid
:
设置是否将表单项标记为无效,如果表单项的值不符合验证规则,可以将其设置为"true"。
【2】参数详解
(1)***widget
:添加标签属性
- 指定字段的控件显示方式。
- 可以使用各种预定义的小部件(widgets)或自定义的小部件类。
- 例如,可以使用
forms.TextInput
将字段以文本框的形式显示出来:
from django import forms
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="用户名",
widget=forms.TextInput(attrs={"class": "form-control"})
)
- 在Django的表单中
widget
参数用于设置表单字段的展示方式或使用自定义的HTML元素来呈现字段。
widget
参数- 可以接受一个Django提供的内置的小部件类(widget class)
- 也可以使用自定义的小部件类。
- 以下是几个常用的小部件类及其作用:
forms.TextInput
:默认的文本输入框小部件。forms.Textarea
:多行文本输入框小部件。forms.CheckboxInput
:复选框小部件。forms.Select
:下拉选择框小部件。forms.RadioSelect
:单选按钮小部件。forms.FileInput
:文件上传小部件。forms.HiddenInput
:隐藏输入框小部件。
- 下面是一个示例代码片段
- 演示如何使用
widget
参数定义一个表单字段的小部件:
- 演示如何使用
from django import forms
class MyForm(forms.Form):
username = forms.CharField(
max_length=100,
widget=forms.TextInput(attrs={'class': 'my-custom-class'})
)
password = forms.CharField(
max_length=100,
widget=forms.PasswordInput()
)
- 上述代码中
username
字段使用自定义的CSS类样式,并将其应用到一个文本输入框小部件中。password
字段则使用了密码输入框小部件。
- 需要注意的是
- 可以通过
attrs
参数传递其他的HTML属性给小部件。 - 例如,在上面的示例中,我们为
username
字段定义了class
属性。
- 可以通过
- 另外
- 如果想要使用自定义的小部件类
- 可以定义一个继承自Django小部件类的类,并将其传递给
widget
参数。例如:
from django import forms
class MyCustomWidget(forms.widgets.TextInput):
# 自定义的小部件类
class MyForm(forms.Form):
my_field = forms.CharField(
widget=MyCustomWidget()
)
- 上述代码中
- 我们创建了一个名为
MyCustomWidget
的自定义小部件类, - 并将其用于
my_field
字段。
- 我们创建了一个名为
- 通过使用
widget
参数- 我们可以对Django表单字段的展示方式进行灵活的定制
- 以满足具体项目的需求。
(2)***required
:是否为必填字段
- 设置字段是否为必填项,默认为
True
。 - 如果设置为
False
,则允许用户提交空值。例如:
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="用户名",
required=False
)
(3)***label
:自定义标签文本
- 指定字段的标签显示文本。
- 可以使用该参数自定义字段在表单中的标签文本。例如:
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="Your Username"
)
(4)***error_messages
:标签不合法的提示信息
- 用于自定义错误消息。
- 可以通过该参数自定义字段验证失败时显示的错误消息。例如:
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="用户名",
error_messages={
"max_length": "用户名最多为8个字符",
"min_length": "用户名至少为3个字符",
"required": "请填写用户名"
}
)
(5)help_text
:描述提醒信息
- 为字段提供帮助文本。
- 可以使用该参数为字段添加描述性的帮助文本,以向用户解释该字段的用途或提供输入建议。例如:
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="用户名",
help_text="请输入3-8个字符的用户名"
)
(6)***validators
:正则表达式
- 用于自定义字段验证器。
- 可以使用该参数添加额外的验证函数来对字段进行更复杂的验证。例如:
from django.core.validators import RegexValidator
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="用户名",
validators=[
RegexValidator(
regex=r"^[a-zA-Z]+$",
message="只允许字母字符"
)
]
)
(7)***initial:默认值
- 可以通过
initial
参数为表单字段设置默认值。 - 该参数指定了在表单加载时显示的初始值。以下是示例代码:
from django import forms
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="用户名",
initial="JohnDoe"
)
- 在上述示例中
username
字段将显示一个文本框- 并且默认值为 "JohnDoe"。
- 另外
- 如果要为多个字段设置默认值
- 可以通过使用
initial
字典来一次性设置多个字段的初始值。例如:
from django import forms
class MyForm(forms.Form):
username = forms.CharField(
max_length=8,
min_length=3,
label="用户名"
)
email = forms.EmailField(
label="邮箱"
)
initial = {
'username': 'JohnDoe',
'email': 'johndoe@example.com'
}
- 可以在实例化表单对象时传递
initial
字典来设置这两个字段的默认值:
form = MyForm(initial={
'username': 'JohnSmith',
'email': 'johnsmith@example.com'
})
- 当表单加载时
username
字段将显示为 "JohnSmith"email
字段将显示为 "johnsmith@example.com"。
- 这样
- 通过使用
initial
参数 - 可以方便地为表单字段设置默认值,提供更好的用户体验。
- 通过使用
【3】其他字段渲染
(1)radioSelect(单选):
- 您可以在表单类中使用
forms.RadioSelect
字段来渲染radioSelect字段。下面是一个案例演示:
from django import forms
class GenderForm(forms.Form):
gender_choices = [
('male', 'Male'),
('female', 'Female'),
]
gender = forms.ChoiceField(choices=gender_choices, widget=forms.RadioSelect)
- 在这个案例中,我们定义了一个名为
GenderForm
的表单类,其中包含了一个gender
字段用于选择性别。 - 通过设置
widget=forms.RadioSelect
,我们将该字段渲染为单选按钮。
(2)单选Select:
- 您可以使用
forms.Select
字段来呈现下拉菜单形式的单选选项。以下是一个案例演示:
from django import forms
class CityForm(forms.Form):
city_choices = [
('1', 'New York'),
('2', 'London'),
('3', 'Tokyo'),
]
city = forms.ChoiceField(choices=city_choices, widget=forms.Select)
- 在这个案例中,我们定义了一个名为
CityForm
的表单类,其中包含了一个city
字段用于选择城市。通过设置widget=forms.Select
,我们将该字段渲染为下拉菜单。
(3)多选Select:
- 使用
forms.SelectMultiple
字段可以渲染多选Select组件。以下是一个案例演示:
from django import forms
class InterestsForm(forms.Form):
interest_choices = [
('coding', 'Coding'),
('gaming', 'Gaming'),
('reading', 'Reading'),
]
interests = forms.MultipleChoiceField(choices=interest_choices, widget=forms.SelectMultiple)
- 在这个案例中,我们定义了一个名为
InterestsForm
的表单类 - 其中包含了一个
interests
字段用于选择兴趣。 - 通过设置
widget=forms.SelectMultiple
,我们将该字段渲染为支持多选的下拉菜单。
(4)单选checkbox:
- 要渲染单选checkbox字段,您可以使用
forms.CheckboxInput
字段。 - 以下是一个案例演示:
from django import forms
class AgreeForm(forms.Form):
agree = forms.BooleanField(widget=forms.CheckboxInput)
- 在这个案例中,我们定义了一个名为
AgreeForm
的表单类 - 其中包含了一个
agree
字段用于接受条款。 - 通过设置
widget=forms.CheckboxInput
,我们将该字段渲染为单选复选框。
(5)多选checkbox:
- 要渲染多选checkbox字段
- 可以使用
forms.CheckboxSelectMultiple
字段。 - 以下是一个案例演示:
from django import forms
class ColorsForm(forms.Form):
color_choices = [
('red', 'Red'),
('green', 'Green'),
('blue', 'Blue'),
]
colors = forms.MultipleChoiceField(choices=color_choices, widget=forms.CheckboxSelectMultiple)
- 在这个案例中,我们定义了一个名为
ColorsForm
的表单类,其中包含了一个colors
字段用于选择颜色。 - 通过设置
widget=forms.CheckboxSelectMultiple
,我们将该字段渲染为多个复选框供用户选择。
【八】forms组件源码简解
切入点
form_obj.is_valid()
def is_valid(self):
"""Return True if the form has no errors, or False otherwise."""
return self.is_bound and not self.errors
- 如果
is_valid
要想返回True- 那么
self.is_bound
要为True self.errors
要为 False
- 那么
【1】self.is_bound
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=None,
empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None):
self.is_bound = data is not None or files is not None
- data 是我们传入的数据
- 只要传入数据有值
- 那么
self.is_bound
一定是True
【2】self.errors
@property
def errors(self):
"""Return an ErrorDict for the data provided for the form."""
if self._errors is None:
self.full_clean()
return self._errors
- forms组件所有的功能基本都出自这个方法
def full_clean(self):
"""
Clean all of self.data and populate self._errors and self.cleaned_data.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields() # 校验字段
self._clean_form()
self._post_clean()
(1)self._clean_fields() 校验字段 + 局部钩子
def _clean_fields(self):
for name, field in self.fields.items(): # 循环获取字段对象
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
# 获取字段对应的值
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value)
self.cleaned_data[name] = value # 将合法的字段添加到字典里
if hasattr(self, 'clean_%s' % name): # 利用反射获取局部钩子函数
value = getattr(self, 'clean_%s' % name)() # 局部钩子需要有返回值
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e) # 添加提示信息
循环获取字段对象
-
局部钩子报错也可以使用
ValidationError
主动抛出异常- 较为繁琐,一般不用
(2)_clean_form
全局钩子
def _clean_form(self):
try:
cleaned_data = self.clean() # 调用父类的clean方法或者自定义的clean方法
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
(3)_post_clean
def _post_clean(self):
"""
An internal hook for performing additional cleaning after form cleaning
is complete. Used for model validation in model forms.
"""
pass
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17560737.html