本文将介绍forms组件的详细情况,特别是关于winform组件的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于03、forms组件、08Djang
本文将介绍forms组件的详细情况,特别是关于winform 组件的相关信息。我们将通过案例分析、数据研究等多种方式,帮助您更全面地了解这个主题,同时也将涉及一些关于03、 forms组件、08 Django组件-Forms组件、12 Django组件-forms组件、12,Django组件-forms组件的知识。
本文目录一览:forms组件(winform 组件)
本文目录
- 1 校验字段功能
- 2 渲染标签功能
- 3 渲染错误信息功能
- 4 组件的参数配置
- 5 局部钩子
- 6 全局钩子
1 校验字段功能
针对一个实例:注册用户讲解。
模型:models.py
class UserInfo(models.Model):
name=models.CharField(max_length=32)
pwd=models.CharField(max_length=32)
email=models.EmailField(
模版文件
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<div>
<label for="user">用户名</label>
<p><input type="text" name="name" id="name"></p>
</div>
<div>
<label for="pwd">密码</label>
<p><input type="password" name="pwd" id="pwd"></p>
</div>
<div>
<label for="r_pwd">确认密码</label>
<p><input type="password" name="r_pwd" id="r_pwd"></p>
</div>
<div>
<label for="email">邮箱</label>
<p><input type="text" name="email" id="email"></p>
</div>
<input type="submit">
</form>
</body>
</html>
视图函数:
# forms组件
from django.forms import widgets
wid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})
class UserForm(forms.Form):
name=forms.CharField(max_length=32,
widget=wid_01
)
pwd=forms.CharField(max_length=32,widget=wid_02)
r_pwd=forms.CharField(max_length=32,widget=wid_02)
email=forms.EmailField(widget=wid_01)
tel=forms.CharField(max_length=32,widget=wid_01)
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]}
print(form.errors.get("name")) # ErrorList ["错误信息",]
return HttpResponse("OK")
form=UserForm()
return render(request,"register.html",locals())
2 渲染标签功能
渲染方式1

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <h3>注册页面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> <form action="" method="post"> {% csrf_token %} <div> <label for="">用户名</label> {{ form.name }} </div> <div> <label for="">密码</label> {{ form.pwd }} </div> <div> <label for="">确认密码</label> {{ form.r_pwd }} </div> <div> <label for=""> 邮箱</label> {{ form.email }} </div> <input type="submit" class="btn btn-default pull-right"> </form> </div> </div> </div> </body> </html>

渲染方式2

<form action="" method="post"> {% csrf_token %} {% for field in form %} <div> <label for="">{{ field.label }}</label> {{ field }} </div> {% endfor %} <input type="submit" class="btn btn-default pull-right"> </form>

渲染方式3

<form action="" method="post"> {% csrf_token %} {{ form.as_p }}
<input type="submit" class="btn btn-default pull-right"> </form>

3 渲染错误信息功能
视图

def register(request):
if request.method=="POST": form=UserForm(request.POST) if form.is_valid(): print(form.cleaned_data) # 所有干净的字段以及对应的值 else: print(form.cleaned_data) # print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]} print(form.errors.get("name")) # ErrorList ["错误信息",] return render(request,"register.html",locals()) form=UserForm() return render(request,"register.html",locals())

模板

<form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div> <label for="">{{ field.label }}</label> {{ field }} <span>{{ field.errors.0 }}</span> </div> {% endfor %} <input type="submit" class="btn btn-default"> </form>

4 组件的参数配置
class Ret(Form):
name = forms.CharField(max_length=10, min_length=2, label=''用户名'',
error_messages={''required'': ''该字段不能为空'', ''invalid'': ''格式错误'', ''max_length'': ''太长'',
''min_length'': ''太短''},
widget=widgets.TextInput(attrs={''class'':''form-control''}))
pwd = forms.CharField(max_length=10, min_length=2, widget=widgets.PasswordInput(attrs={''class'':''form-control''}))
email = forms.EmailField(label=''邮箱'', error_messages={''required'': ''该字段不能为空'', ''invalid'': ''格式错误''})
5 局部钩子
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
def clean_name(self):
val=self.cleaned_data.get("name")
ret=UserInfo.objects.filter(name=val)
if not ret:
return val
else:
raise ValidationError("该用户已注册!")
def clean_tel(self):
val=self.cleaned_data.get("tel")
if len(val)==11:
return val
else:
raise ValidationError("手机号格式错误")
6 全局钩子
def clean(self):
pwd=self.cleaned_data.get(''pwd'')
r_pwd=self.cleaned_data.get(''r_pwd'')
if pwd and r_pwd:
if pwd==r_pwd:
return self.cleaned_data
else:
raise ValidationError(''两次密码不一致'')
else:
return self.cleaned_data
pwd_err=my_form.errors.get(''__all__'')


from django import forms
from django.forms import widgets
from app01.models import UserInfo
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
class UserForm(forms.Form):
name=forms.CharField(min_length=4,label="用户名",error_messages={"required":"该字段不能为空"},
widget=widgets.TextInput(attrs={"class":"form-control"})
)
pwd=forms.CharField(min_length=4,label="密码",
widget=widgets.PasswordInput(attrs={"class":"form-control"})
)
r_pwd=forms.CharField(min_length=4,label="确认密码",error_messages={"required":"该字段不能为空"},widget=widgets.TextInput(attrs={"class":"form-control"}))
email=forms.EmailField(label="邮箱",error_messages={"required":"该字段不能为空","invalid":"格式错误"},widget=widgets.TextInput(attrs={"class":"form-control"}))
tel=forms.CharField(label="手机号",widget=widgets.TextInput(attrs={"class":"form-control"}))
def clean_name(self):
val=self.cleaned_data.get("name")
ret=UserInfo.objects.filter(name=val)
if not ret:
return val
else:
raise ValidationError("该用户已注册!")
def clean_tel(self):
val=self.cleaned_data.get("tel")
if len(val)==11:
return val
else:
raise ValidationError("手机号格式错误")
def clean(self):
pwd=self.cleaned_data.get(''pwd'')
r_pwd=self.cleaned_data.get(''r_pwd'')
if pwd and r_pwd:
if pwd==r_pwd:
return self.cleaned_data
else:
raise ValidationError(''两次密码不一致'')
else:
return self.cleaned_data


from django.shortcuts import render,HttpResponse
# Create your views here.
from app01.myforms import *
def reg(request):
if request.method=="POST":
print(request.POST)
#form=UserForm({"name":"yu","email":"123@qq.com","xxxx":"alex"})
form=UserForm(request.POST) # form表单的name属性值应该与forms组件字段名称一致
print(form.is_valid()) # 返回布尔值
if form.is_valid():
print(form.cleaned_data) # {"name":"yuan","email":"123@qq.com"}
else:
print(form.cleaned_data) # {"email":"123@qq.com"}
# print(form.errors) # {"name":[".........."]}
# print(type(form.errors)) # ErrorDict
# print(form.errors.get("name"))
# print(type(form.errors.get("name"))) # ErrorList
# print(form.errors.get("name")[0])
# 全局钩子错误
#print("error",form.errors.get("__all__")[0])
errors=form.errors.get("__all__")
return render(request,"reg.html",locals())
''''''
form.is_valid() :返回布尔值
form.cleaned_data :{"name":"yuan","email":"123@qq.com"}
form.errors :{"name":[".........."]}
''''''
form=UserForm()
return render(request,"reg.html",locals())


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.error{
color: red;
}
</style>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
{#<h3>简单form</h3>#}
{##}
{##}
{#<form action="" method="post" novalidate>#}
{# {% csrf_token %}#}
{# <p>用户名<input type="text" name="name"></p>#}
{# <p>密码 <input type="text" name="pwd"></p>#}
{# <p>确认密码 <input type="text" name="r_pwd"></p>#}
{# <p>邮箱 <input type="text" name="email"></p>#}
{# <p>手机号 <input type="text" name="tel"></p>#}
{# <input type="submit">#}
{##}
{#</form>#}
<hr>
<h3>forms组件渲染方式1</h3>
<form action="" method="post" novalidate>
{% csrf_token %}
<p>{{ form.name.label }}
{{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span>
</p>
<p>{{ form.pwd.label }}
{{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span>
</p>
<p>确认密码
{{ form.r_pwd }} <span class="pull-right error">{{ form.r_pwd.errors.0 }}</span><span class="pull-right error">{{ errors.0 }}</span>
</p>
<p>邮箱 {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span></p>
<p>手机号 {{ form.tel }} <span class="pull-right error">{{ form.tel.errors.0 }}</span></p>
<input type="submit">
</form>
{#<h3>forms组件渲染方式2</h3>#}
{##}
{#<form action="" method="post" novalidate>#}
{# {% csrf_token %}#}
{##}
{# {% for field in form %}#}
{##}
{# <div>#}
{# <label for="">{{ field.label }}</label>#}
{# {{ field }}#}
{# </div>#}
{##}
{# {% endfor %}#}
{##}
{# <input type="submit">#}
{#</form>#}
{##}
{#<h3>forms组件渲染方式3</h3>#}
{##}
{#<form action="" method="post">#}
{# {% csrf_token %}#}
{##}
{# {{ form.as_p }}#}
{##}
{# <input type="submit">#}
{#</form>#}
</div>
</div>
</div>
</body>
</html>
03、 forms组件
1、校验字段功能
1、reg页面准备
models
from django.db import models
class UserInfo(models.Model):
useranme = models.CharField(max_length=32)
password = models.CharField(max_length=32)
email = models.EmailField()
telephone = models.CharField(max_length=32)
生成数据表
C:\PycharmProjects\formsdemo>python manage.py makemigrations
C:\PycharmProjects\formsdemo>python manage.py migrate
主url
from django.contrib import admin
from django.urls import path, re_path, include
urlpatterns = [
path(''admin/'', admin.site.urls),
re_path(r''^'', include((''app01.urls'', ''app01'')))
]
url
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
re_path(r''reg/$'', views.reg, name=''reg''),
]
模板层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>用户名 <input type="text" name="user"></p>
<p>密码 <input type="password" name="pwd"></p>
<p>确认密码 <input type="password" name="_pwd"></p>
<p>邮箱 <input type="text" name="email"></p>
<p>手机号 <input type="text" name="tel"></p>
<input type="submit" value="注册">
</form>
</body>
</html>
view
from django.shortcuts import render, HttpResponse
def reg(request):
if request.method == ''POST'':
print(request.POST)
return HttpResponse(''注册成功'')
return render(request, ''reg.html'')
<QueryDict: {''csrfmiddlewaretoken'': [''5EwZsUEKRVj836bplmS03PVruttZhG''], ''user'': [''alex''], ''pwd'': [''123''], ''_pwd'': [''123''], ''email'': [''123@qq.com''], ''tel'': [''1234566778'']}>
2、定义规则


from django import forms # 导入forms组件
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4, max_length=10)
email = forms.EmailField()
3、form.is_valid()
reg视图代码
from django.shortcuts import render, HttpResponse
# Create your views here.
from django import forms # 导入forms组件
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4, max_length=10)
email = forms.EmailField()
def reg(request):
if request.method == ''POST'':
# 1.form.is_valid() 是否通过验证
form = UserForm({''name'': ''alex'', ''email'': ''123@qq.com''})
print(form.is_valid()) # True 返回布尔值
form = UserForm({''name'': ''a'', ''email'': ''123''})
print(form.is_valid()) # False
form = UserForm({''name'': ''alex'', ''email'': ''123@qq.com'', ''age'': ''33''})
print(form.is_valid()) # True
#
form = UserForm({''name'': ''alex'', ''xxx'': ''123@qq.com''})
print(form.is_valid()) # False
form = UserForm({''name'': ''a'', ''email'': ''123'', ''xxx'': ''123''})
print(form.is_valid()) # False
if form.is_valid():
pass
else:
pass
"""
forms组件只会校验定义过的字段,
定义的data,必须满足条件且存在 True
多余的字段,不影响
"""
return HttpResponse(''注册成功'')
return render(request, ''reg.html'')
4、form.cleaned_data:清洗过的数据 form.errors:报错信息
from django.shortcuts import render, HttpResponse
# Create your views here.
from django import forms # 导入forms组件
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4, max_length=10)
email = forms.EmailField()
def reg(request):
if request.method == ''POST'':
# 1.form.is_valid() 是否通过验证
form = UserForm({''name'': ''alex'', ''email'': ''123@qq.com''})
print(form.is_valid()) # True 返回布尔值
form = UserForm({''name'': ''a'', ''email'': ''123''})
print(form.is_valid()) # False
form = UserForm({''name'': ''alex'', ''email'': ''123@qq.com'', ''age'': ''33''})
print(form.is_valid()) # True
#
form = UserForm({''name'': ''alex'', ''xxx'': ''123@qq.com''})
print(form.is_valid()) # False
form = UserForm({''name'': ''a'', ''email'': ''123'', ''xxx'': ''123''})
print(form.is_valid()) # False
# 2. form.cleaned_data
# form.errors
if form.is_valid():
print(''cleaned_data1:'', form.cleaned_data)
print(''errors1:'', form.errors)
else:
print(''cleaned_data2:'', form.cleaned_data)
print(''errors2:'', form.errors)
# 取出error——msg
print(form.errors.get("name"))
print(type(form.errors.get("name"))) # ErrorList
print(form.errors.get("name")[0]) # Ensure this value has at least 4 characters (it has 1).
"""
if 所有字段校验成功,则
form.cleaned_data: {''name'': ''alex'', ''email'': ''123@qq.com''} 只保留定义过的字段
form.errors: 空
if 校验不成功,则
form.cleaned_data:{''name'': ''alex''} 只保留定义过的字段,
form.errors: <li>email<ul> 定义过的字段,未通过的信息
(cleaned_data + errors 之和,必定是定义过的所有字段)
"""
return HttpResponse(''注册成功'')
return render(request, ''reg.html'')
(1)验证通过
验证字段
(2)验证不通过
示例1:验证字段
示例2:验证字段
5、取出 forms.error错误信息
6、完整演示代码
url
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
re_path(r''reg_test/$'', views.reg_test, name=''reg_test''),
]
view
from django.shortcuts import render, HttpResponse
from django import forms # 导入forms组件
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4, max_length=10)
pwd = forms.CharField(min_length=4)
_pwd = forms.CharField(min_length=4)
email = forms.EmailField()
tel = forms.CharField()
def reg_test(request):
if request.method == "POST":
form = UserForm(request.POST) # from表单的name属性值应该与forms组件字段名称一致
print(form)
if form.is_valid():
print(form.cleaned_data)
return HttpResponse("注册成功")
else:
print(form.cleaned_data) # {''email'': ''123@qq.com'', ''tel'': ''a3456789''}
print(form.errors) # <ul><li>name<ul><li>This fie
print(type(form.errors)) # ErrorList
print(form.errors.get(''name'')) # <ul><li>This field is required.</li></ul>
print(type(form.errors.get(''name''))) # ErrorList
print(form.errors.get(''name'')[0]) # This field is required.
return HttpResponse("注册失败")
return render(request, ''reg.html'')
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>用户名 <input type="text" name="user"></p>
<p>密码 <input type="password" name="pwd"></p>
<p>确认密码 <input type="password" name="_pwd"></p>
<p>邮箱 <input type="text" name="email"></p>
<p>手机号 <input type="text" name="tel"></p>
<input type="submit" value="注册">
</form>
</body>
</html>
注意点:
from表单的name属性值应该与forms组件字段名称一致


<tr><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="name" maxlength="10" minlength="4" required id="id_name" /></td></tr>
<tr><th><label for="id_pwd">Pwd:</label></th><td><ul class="errorlist"><li>Ensure this value has at least 4 characters (it has 3).</li></ul><input type="text" name="pwd" value="123" minlength="4" required id="id_pwd" /></td></tr>
<tr><th><label for="id__pwd"> pwd:</label></th><td><ul class="errorlist"><li>Ensure this value has at least 4 characters (it has 3).</li></ul><input type="text" name="_pwd" value="123" minlength="4" required id="id__pwd" /></td></tr>
<tr><th><label for="id_email">Email:</label></th><td><input type="email" name="email" value="123@qq.com" required id="id_email" /></td></tr>
<tr><th><label for="id_tel">Tel:</label></th><td><input type="text" name="tel" value="a3456789" required id="id_tel" /></td></tr>
2、forms组件的渲染标签功能
1、方式1: 用户名 {{ form.name }}
views
from django.shortcuts import render, HttpResponse
from django import forms # 导入forms组件
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4, max_length=10)
pwd = forms.CharField(min_length=4)
r_pwd = forms.CharField(min_length=4)
email = forms.EmailField()
tel = forms.CharField()
def reg_html(request):
form = UserForm()
return render(request, ''reg_html.html'', locals())
reg.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>forms组件渲染方式1</h3>
<form action="" method="post">
{% csrf_token %}
<p>{{ form.name.label }} {{ form.name }}</p>
<p>{{ form.pwd.label }} {{ form.pwd }}</p>
<p>{{ form.r_pwd.label }} {{ form.r_pwd }}</p>
<p>{{ form.email.label }} {{ form.email }}</p>
<p>{{ form.tel.label }} {{ form.tel }}</p>
<input type="submit" value="注册">
</form>
</body>
</html>
原生标签与渲染标签的区别
2、方式2:推荐
views视图
from django.shortcuts import render, HttpResponse
# Create your views here.
from django import forms # 导入forms组件
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4, max_length=10, label="用户名")
pwd = forms.CharField(min_length=4, label="密码")
r_pwd = forms.CharField(min_length=4, label="确认密码")
email = forms.EmailField(label="邮箱")
tel = forms.CharField(label="电话")
def reg_html(request):
form = UserForm()
return render(request, ''reg_html.html'', locals())
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>forms组件渲染方式2:推荐</h3>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<p><label for="">{{ field.label }}:</label>{{ field }}</p>
{% endfor %}
</form>
</body>
</html>
3、方式3: {{ form.as_p }} 不灵活
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>forms组件渲染方式3 </h3>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<hr>
{{ form.as_ul }}
<hr>
{{ form.as_table }}
</form>
</body>
</html>
3、显示error与重置输入信息功能
views视图
from django.shortcuts import render, HttpResponse
# Create your views here.
from django import forms # 导入forms组件
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4, max_length=10, label="用户名")
pwd = forms.CharField(min_length=4, label="密码")
r_pwd = forms.CharField(min_length=4, label="确认密码")
email = forms.EmailField(label="邮箱")
tel = forms.CharField(label="电话")
def reg_html(request):
if request.method == ''POST'':
form = UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有感觉的字段以及对应的值
else:
print(form.cleaned_data)
print(form.errors) # ErrorDict: {"校验错误的字段":["错误信息"]}
print(form.errors.get(''name'')) # ErrorList {''错误信息'',}
return render(request, ''reg_html.html'', locals())
form = UserForm()
return render(request, ''reg_html.html'', locals())
html
novalidate 当提交表单时不对表单数据(输入)进行验证
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>forms组件渲染方式1</h3>
{# novalidate 当提交表单时不对表单数据(输入)进行验证#}
<form action="" method="post" novalidate>
{% csrf_token %}
<p>{{ form.name.label }} {{ form.name }} <span>{{ form.name.errors.0 }}</span></p>
<p>{{ form.pwd.label }} {{ form.pwd }} <span>{{ form.pwd.errors.0 }}</span></p>
<p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span>{{ form.r_pwd.errors.0 }}</span></p>
<p>{{ form.email.label }} {{ form.email }} <span>{{ form.email.errors.0 }}</span></p>
<p>{{ form.tel.label }} {{ form.tel }} <span>{{ form.tel.errors.0 }}</span></p>
<input type="submit" value="注册">
</form>
</body>
</html>
4、forms组件的参数配置
forms组件 代码
from django.shortcuts import render, HttpResponse
# Create your views here.
from django import forms # 导入forms组件
from django.forms import widgets # HTML Widget classes
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4,
max_length=10,
label="用户名",
error_messages={''required'': ''该字段不能为空'', ''min_length'': ''不能少于4个字符''},
# widget=widgets.TextInput(), # 定制Html控件
widget=widgets.TextInput(attrs={''class'': ''form-control''}), # html控件添加样式
)
pwd = forms.CharField(min_length=4,
label="密码",
# widget=widgets.PasswordInput(), # 定制Html控件
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
error_messages={''required'': ''该字段不能为空''},
)
r_pwd = forms.CharField(min_length=4,
label="确认密码",
error_messages={''required'': ''该字段不能为空''},
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
)
email = forms.EmailField(label="邮箱",
error_messages={''required'': ''该字段不能为空'', ''invalid'': ''格式错误''},
widget=widgets.EmailInput(attrs={''class'': ''form-control''})
)
tel = forms.CharField(label="电话",
error_messages={''required'': ''该字段不能为空 ''},
widget=widgets.TextInput(attrs={''class'': ''form-control''})
)
def reg_html(request):
if request.method == ''POST'':
form = UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有清洗的字段以及对应的值
else:
print(form.cleaned_data)
print(form.errors) # ErrorDict: {"校验错误的字段":["错误信息"]}
print(form.errors.get(''name'')) # ErrorList {''错误信息'',}
return render(request, ''reg_html.html'', locals())
form = UserForm()
return render(request, ''reg_html.html'', locals())
加入bootstrap后的 html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style type="text/css">
span{
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<h3>forms组件渲染方式1</h3>
{# novalidate 当提交表单时不对表单数据(输入)进行验证#}
<form action="" method="post" novalidate>
{% csrf_token %}
<p>{{ form.name.label }} {{ form.name }} <span>{{ form.name.errors.0 }}</span></p>
<p>{{ form.pwd.label }} {{ form.pwd }} <span>{{ form.pwd.errors.0 }}</span></p>
<p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span>{{ form.r_pwd.errors.0 }}</span></p>
<p>{{ form.email.label }} {{ form.email }} <span>{{ form.email.errors.0 }}</span></p>
<p>{{ form.tel.label }} {{ form.tel }} <span>{{ form.tel.errors.0 }}</span></p>
<input type="submit" value="注册">
</form>
</div>
</div>
</div>
</body>
</html>
5、forms组件的局部钩子
1、ValidationError:验证错误
models
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
email = models.EmailField()
tel = models.CharField(max_length=32)
froms组件,如何进行验证错误?
from app01.models import UserInfo # 导入user表
from django.core.exceptions import ValidationError # 导入验证错误
from django import forms # 导入forms组件
from django.forms import widgets # HTML Widget classes
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4,
max_length=10,
label="用户名",
error_messages={''required'': ''该字段不能为空'', ''min_length'': ''不能少于4个字符''},
# widget=widgets.TextInput(), # 定制Html控件
widget=widgets.TextInput(attrs={''class'': ''form-control''}), # html控件添加样式
)
pwd = forms.CharField(min_length=4,
label="密码",
# widget=widgets.PasswordInput(), # 定制Html控件
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
error_messages={''required'': ''该字段不能为空''},
)
r_pwd = forms.CharField(min_length=4,
label="确认密码",
error_messages={''required'': ''该字段不能为空''},
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
)
email = forms.EmailField(label="邮箱",
error_messages={''required'': ''该字段不能为空'', ''invalid'': ''格式错误''},
widget=widgets.EmailInput(attrs={''class'': ''form-control''})
)
tel = forms.CharField(label="电话",
error_messages={''required'': ''该字段不能为空 ''},
widget=widgets.TextInput(attrs={''class'': ''form-control''})
)
# 验证用户名
def clean_name(self):
val = self.cleaned_data.get("name") # 获取清洗后的字段中的name
ret = UserInfo.objects.filter(name=val) # 数据库中的user
if not ret:
return ret
else:
raise ValidationError(''该用户已经注册'') # 验证错误
forms组件源码
vies视图中
def reg_html(request):
if request.method == ''POST'':
form = UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有清洗的字段以及对应的值
else:
print(form.cleaned_data)
print(form.errors) # ErrorDict: {"校验错误的字段":["错误信息"]}
print(form.errors.get(''name'')) # ErrorList {''错误信息'',}
return render(request, ''reg_html.html'', locals())
form = UserForm()
return render(request, ''reg_html.html'', locals())
模板层:不变


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style type="text/css">
span{
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<h3>forms组件渲染方式1</h3>
{# novalidate 当提交表单时不对表单数据(输入)进行验证#}
<form action="" method="post" novalidate>
{% csrf_token %}
<p>{{ form.name.label }} {{ form.name }} <span>{{ form.name.errors.0 }}</span></p>
<p>{{ form.pwd.label }} {{ form.pwd }} <span>{{ form.pwd.errors.0 }}</span></p>
<p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span>{{ form.r_pwd.errors.0 }}</span></p>
<p>{{ form.email.label }} {{ form.email }} <span>{{ form.email.errors.0 }}</span></p>
<p>{{ form.tel.label }} {{ form.tel }} <span>{{ form.tel.errors.0 }}</span></p>
<input type="submit" value="注册">
</form>
</div>
</div>
</div>
</body>
</html>
2、校验方法:clean_name 源码
_clean_fields 方法的源码
def _clean_fields(self):
for name, field in self.fields.items(): # {"name": name规则对象,"pwd": pwd规则对象...}
# 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 # 执行 def clean_name(self):
except ValidationError as e:
self.add_error(name, e)
3、局部钩子函数如何校验
1、forms代码:校验name,tel
from app01.models import UserInfo # 导入user表
from django.core.exceptions import ValidationError # 导入验证错误
from django import forms # 导入forms组件
from django.forms import widgets # HTML Widget classes
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4,
max_length=10,
label="用户名",
error_messages={''required'': ''该字段不能为空'', ''min_length'': ''不能少于4个字符''},
# widget=widgets.TextInput(), # 定制Html控件
widget=widgets.TextInput(attrs={''class'': ''form-control''}), # html控件添加样式
)
pwd = forms.CharField(min_length=4,
label="密码",
# widget=widgets.PasswordInput(), # 定制Html控件
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
error_messages={''required'': ''该字段不能为空''},
)
r_pwd = forms.CharField(min_length=4,
label="确认密码",
error_messages={''required'': ''该字段不能为空''},
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
)
email = forms.EmailField(label="邮箱",
error_messages={''required'': ''该字段不能为空'', ''invalid'': ''格式错误''},
widget=widgets.EmailInput(attrs={''class'': ''form-control''})
)
tel = forms.CharField(label="电话",
error_messages={''required'': ''该字段不能为空 ''},
widget=widgets.TextInput(attrs={''class'': ''form-control''})
)
# 验证用户名
def clean_name(self):
val = self.cleaned_data.get("name") # 获取清洗后的字段中的name
ret = UserInfo.objects.filter(name=val) # 数据库中的user
if not ret:
return ret
else:
raise ValidationError(''该用户已经注册'') # 验证错误
# 验证手机号
def clean_tel(self):
val = self.cleaned_data.get("tel")
if len(val) == 11:
return val
else:
raise ValidationError("手机号格式错误")
示例1:校验错误
示例2:校验正确
4、全局钩子校验
1、全局钩子clean 源码剖析
2、直接覆盖父类的clean方法,全局钩子
froms组件继承的是BaseFrom类,所以可以直接覆盖父类的clean方法,进行全局钩子校验
views视图
from django.shortcuts import render, HttpResponse
from app01.models import UserInfo # 导入user表
from django.core.exceptions import ValidationError # 导入验证错误
from django import forms # 导入forms组件
from django.forms import widgets # HTML Widget classes
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4,
max_length=10,
label="用户名",
error_messages={''required'': ''该字段不能为空'', ''min_length'': ''不能少于4个字符''},
# widget=widgets.TextInput(), # 定制Html控件
widget=widgets.TextInput(attrs={''class'': ''form-control''}), # html控件添加样式
)
pwd = forms.CharField(min_length=4,
label="密码",
# widget=widgets.PasswordInput(), # 定制Html控件
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
error_messages={''required'': ''该字段不能为空''},
)
r_pwd = forms.CharField(min_length=4,
label="确认密码",
error_messages={''required'': ''该字段不能为空''},
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
)
email = forms.EmailField(label="邮箱",
error_messages={''required'': ''该字段不能为空'', ''invalid'': ''格式错误''},
widget=widgets.EmailInput(attrs={''class'': ''form-control''})
)
tel = forms.CharField(label="电话",
error_messages={''required'': ''该字段不能为空 ''},
widget=widgets.TextInput(attrs={''class'': ''form-control''})
)
# 直接覆盖父类的clean方法, 全局钩子
def clean(self):
pwd = self.cleaned_data.get("pwd")
r_pwd = self.cleaned_data.get("r_pwd")
if pwd == r_pwd:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致")
def reg_html(request):
if request.method == ''POST'':
form = UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # {"name": "alex", "pwd": "123",...}
else:
print(form.cleaned_data) # # 所有清洗的字段以及对应的值
print(form.errors) # ErrorDict: {"校验错误的字段":["错误信息"]}
print(form.errors.get(''name'')) # ErrorList {''错误信息'',}
# 获取全局钩子的error
print("error:", form.errors.get("__all__")[0])
errors = form.errors.get("__all__")
return render(request, ''reg_html.html'', locals())
form = UserForm()
return render(request, ''reg_html.html'', locals())
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style type="text/css">
span{
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<h3>forms组件渲染方式1</h3>
{# novalidate 当提交表单时不对表单数据(输入)进行验证#}
<form action="" method="post" novalidate>
{% csrf_token %}
<p>{{ form.name.label }} {{ form.name }} <span>{{ form.name.errors.0 }}</span></p>
<p>{{ form.pwd.label }} {{ form.pwd }} <span>{{ form.pwd.errors.0 }}</span></p>
{# <p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span>{{ form.r_pwd.errors.0 }}</span></p> 局部钩子错误#}
<p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span class="pull-right error">{{ errors.0 }}</span></p>
<p>{{ form.email.label }} {{ form.email }} <span>{{ form.email.errors.0 }}</span></p>
<p>{{ form.tel.label }} {{ form.tel }} <span>{{ form.tel.errors.0 }}</span></p>
<input type="submit" value="注册">
</form>
</div>
</div>
</div>
</body>
</html>
重点
# 接收全局钩子error errors.0
# bootstarp样式 <span>
<p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span class="pull-right error">{{ errors.0 }}</span></p>
5、密码,确认密码,顺序
# 每个字段校验完,在走全局方法
比如,先判断密码是否正确,符合规则再判断,两次密码是否一致
如果没有pwd传入进来的话,
# 直接覆盖父类的clean方法, 全局钩子
def clean(self):
pwd = self.cleaned_data.get("pwd")
r_pwd = self.cleaned_data.get("r_pwd")
# 先判断是否接受到pwd,r_pwd的值
if pwd and r_pwd:
if pwd == r_pwd:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致")
else:
return self.cleaned_data
6、form.is_valid 调用源码
form.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
def errors(self):
"""Return an ErrorDict for the data provided for the form."""
if self._errors is None:
self.full_clean()
return self._errors
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()
# 局部钩子
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)
# 全局钩子调用顺序
def _clean_form(self):
try:
cleaned_data = self.clean() # 全局钩子
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
def clean(self):
"""
Hook for doing any extra form-wide cleaning after Field.clean() has been
called on every field. Any ValidationError raised by this method will
not be associated with a particular field; it will have a special-case
association with the field named ''__all__''.
"""
return self.cleaned_data
@html_safe
class BaseForm:
"""
The main implementation of all the Form logic. Note that this class is
different than Form. See the comments by the Form class for more info. Any
improvements to the form API should be made to this class, not to the Form
class.
"""
def clean(self):
pass
7、总结:forms组件解耦,调用冲突
myforms.py 组件代码
from app01.models import UserInfo # 导入user表
from django.core.exceptions import ValidationError # 导入验证错误
from django import forms # 导入forms组件
from django.forms import widgets # HTML Widget classes
# 定义校验规则
class UserForm(forms.Form):
name = forms.CharField(min_length=4,
max_length=10,
label="用户名",
error_messages={''required'': ''该字段不能为空'', ''min_length'': ''不能少于4个字符''},
# widget=widgets.TextInput(), # 定制Html控件
widget=widgets.TextInput(attrs={''class'': ''form-control''}), # html控件添加样式
)
pwd = forms.CharField(min_length=4,
label="密码",
# widget=widgets.PasswordInput(), # 定制Html控件
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
error_messages={''required'': ''该字段不能为空''},
)
r_pwd = forms.CharField(min_length=4,
label="确认密码",
error_messages={''required'': ''该字段不能为空''},
widget=widgets.PasswordInput(attrs={''class'': ''form-control''}),
)
email = forms.EmailField(label="邮箱",
error_messages={''required'': ''该字段不能为空'', ''invalid'': ''格式错误''},
widget=widgets.EmailInput(attrs={''class'': ''form-control''})
)
tel = forms.CharField(label="电话",
error_messages={''required'': ''该字段不能为空 ''},
widget=widgets.TextInput(attrs={''class'': ''form-control''})
)
# 验证用户名
def clean_name(self):
val = self.cleaned_data.get("name") # 获取清洗后的字段中的name
ret = UserInfo.objects.filter(name=val) # 数据库中的user
if not ret:
return ret
else:
raise ValidationError(''该用户已经注册'') # 验证错误
# 验证手机号
def clean_tel(self):
val = self.cleaned_data.get("tel")
if len(val) == 11:
return val
else:
raise ValidationError("手机号格式错误")
# 直接覆盖父类的clean方法, 全局钩子
def clean(self):
pwd = self.cleaned_data.get("pwd")
r_pwd = self.cleaned_data.get("r_pwd")
# 先判断是否接受到pwd,r_pwd的值
if pwd and r_pwd:
if pwd == r_pwd:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致")
else:
return self.cleaned_data
url
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
re_path(r''reg_html/$'', views.reg_html, name=''reg_html''),
]
views视图代码
from django.shortcuts import render
from app01.myforms import *
def reg_html(request):
if request.method == ''POST'':
form = UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # {"name": "alex", "pwd": "123",...}
else:
print(form.cleaned_data) # # 所有清洗的字段以及对应的值
print(form.errors) # ErrorDict: {"校验错误的字段":["错误信息"]}
print(form.errors.get(''name'')) # ErrorList {''错误信息'',}
# 获取全局钩子的error
# print("error:", form.errors.get("__all__")[0])
errors = form.errors.get("__all__")
return render(request, ''reg_html.html'', locals())
form = UserForm()
return render(request, ''reg_html.html'', locals())
模板层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style type="text/css">
span{
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<h3>forms组件渲染方式1</h3>
{# novalidate 当提交表单时不对表单数据(输入)进行验证#}
<form action="" method="post" novalidate>
{% csrf_token %}
<p>{{ form.name.label }} {{ form.name }} <span class="pull-right error">{{ form.name.errors.0 }}</span></p>
<p>{{ form.pwd.label }} {{ form.pwd }} <span class="pull-right error">{{ form.pwd.errors.0 }}</span></p>
{# 局部钩子错误 <p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span>{{ form.r_pwd.errors.0 }}</span></p> #}
{# 全局钩子错误#}<p>{{ form.r_pwd.label }} {{ form.r_pwd }} <span>{{ errors.0 }}</span></p>
<p>{{ form.email.label }} {{ form.email }} <span class="pull-right error">{{ form.email.errors.0 }}</span></p>
<p>{{ form.tel.label }} {{ form.tel }} <span class="pull-right error">{{ form.tel.errors.0 }}</span></p>
<input type="submit" value="注册">
</form>
</div>
</div>
</div>
</body>
</html>
08 Django组件-Forms组件
Django的Forms组件主要有以下几大功能:
- 页面初始化,生成HTML标签
- 校验用户数据(显示错误信息)
- HTML Form提交保留上次提交数据
一、小试牛刀
1、定义Form类
from django import forms
class EmpForm(forms.Form):
name = forms.CharField(min_length=5, label="姓名", error_messages={"required": "该字段不能为空!",
"min_length": "用户名太短。"})
age = forms.IntegerField(label="年龄")
salary = forms.DecimalField(max_digits=5, decimal_places=2, label="工资")


class Emp(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
salary = models.DecimalField(max_digits=8, decimal_places=2)
2、设计url与视图对应关系


from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path(''admin/'', admin.site.urls),
path(''index/'', views.index),
path(''add_emp/'', views.add_emp),
]
3、视图函数


from django.shortcuts import render,redirect
from app01 import models
# Create your views here.
from django.http.response import HttpResponse
def index(request):
return HttpResponse("okok!")
from app01.MyForms import EmpForm
def add_emp(request):
if request.method == "GET":
form = EmpForm() # 初始化form对象
return render(request, "add_emp.html", {"form":form}) #传送一个实例对象
else:
form = EmpForm(request.POST) # 将数据传给form对象
if form.is_valid(): # 进行校验
data = form.cleaned_data # 校验通过的数据,字典
print(data)#{''name'': ''alex01'', ''age'': 12, ''salary'': Decimal(''21'')}
models.Emp.objects.create(**data)
return redirect("/index/")
else: # 校验失败
print(form.errors) #<ul><li>name<ul><li>用户名太短。</li></ul></li><li>age<ul><li>This field is required.</li></ul></li><li>salary<ul><li>This field is required.</li></ul></li></ul>
''''''<ul>
<li>
name
<ul>
<li>用户名太短。</li> 可能会有多个错误
...
</ul>
</li>
<li>age<ul><li>This field is required.</li></ul></li>
<li>salary<ul><li>This field is required.</li></ul></li>
</ul>''''''
return render(request, "add_emp.html", {"form": form}) #传送一个带错误信息实例对象
4、模板文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h3>添加员工</h3>
{#1、自己手动写HTML页面#}
{#<form action="" method="post">#}
{# <p>姓名:<input type="text" name="name"></p>#}
{# <p>年龄:<input type="text" name="age"></p>#}
{# <p>工资:<input type="text" name="salary"></p>#}
{# <input type="submit">#}
{#</form>#}
{#2、通过form对象的as_p方法实现#}
{#<form action="" method="post" novalidate>#}
{# {% csrf_token %}#}
{# {{ form.as_p }}#}
{# <input type="submit">#}
{#</form>#}
</body>
</html>


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" novalidate> <!--novalidate novalidate 属性规定当提交表单时不对其进行验证。如果使用该属性,则表单(浏览器)不会验证表单的输入。-->
{% csrf_token %}
<div>
<label for="id_{{ form.name.name }}">姓名</label> <!--form.name.name等价于表单类的字段名 等价于 name age salary ... -->
{{ form.name }} <span>{{ form.name.errors.0 }}<!--form.name.errors.0 表单类可能定义了多个错误信息 先显示第一个 解决完 --></span>
</div>
<div>
<label for="id_{{ form.age.name }}">年龄</label>
{{ form.age }} <span>{{ form.age.errors.0 }}</span>
</div>
<div>
<label for="id_salary">工资</label>
{{ form.salary }} <span>{{ form.salary.errors.0 }}</span>
</div>
<input type="submit">
</form>
<!--以下为浏览器显示的页面-->
<!--<form action="" method="post" novalidate>-->
<!--<input type="hidden" name="csrfmiddlewaretoken" value="oVa3cSt17v5ie9bQBWo3MEBiv32zysmCAvY2QMCGHk3aDyaMFPhnR8ikIniQoA0H">-->
<!--<div>-->
<!--<label for="id_name">姓名</label>-->
<!--<input type="text" name="name" value="alex" minlength="5" required id="id_name"> <span>用户名太短。</span>-->
<!--</div>-->
<!--<div>-->
<!--<label for="id_age">年龄</label>-->
<!--<input type="number" name="age" required id="id_age"> <span>This field is required.</span>-->
<!--</div>-->
<!--<div>-->
<!--<label for="id_salary">工资</label>-->
<!--<input type="number" name="salary" step="0.01" required id="id_salary"> <span>This field is required.</span>-->
<!--</div>-->
<!--<input type="submit">-->
<!--</form>-->
</body>
</html>


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %} <!-- 遍历form表单类所有的字段信息 field 相当于 第三种方式的 form.name/form.age/form.salary-->
<div>
<label for="id_{{ field.name }}">{{ field.label }}</label>
<!--发现:name = forms.CharField(min_length=5, label="姓名", error_messages={"required": "该字段不能为空!","min_length": "用户名太短。"}) 表单类定义的字段属性 字段都可以在前端调用 field.label -->
{{ field }} <span>{{ field.errors.0 }}</span>
<!-- field.errors.0 跟这个error_messages={"required": "该字段不能为空!","min_length": "用户名太短。"} 有关联-->
</div>
{% endfor %}
<input type="submit">
</form>
</body>
</html>
附:Django内置的字段及属性


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)
max_length=None, 最大长度
min_length=None, 最小长度
strip=True 是否移除用户输入空白
IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值
FloatField(IntegerField)
...
DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度
BaseTemporalField(Field)
input_formats=None 时间格式化
DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
DurationField(Field) 时间间隔:%d %H:%M:%S.%f
...
RegexField(CharField)
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={''invalid'': ''...''}
EmailField(CharField)
...
FileField(Field)
allow_empty_file=False 是否允许空文件
ImageField(FileField)
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
- form表单中 enctype="multipart/form-data"
- view函数中 obj = MyForm(request.POST, request.FILES)
URLField(Field)
...
BooleanField(Field)
...
NullBooleanField(BooleanField)
...
ChoiceField(Field)
...
choices=(), 选项,如:choices = ((0,''上海''),(1,''北京''),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='''', 帮助提示
ModelChoiceField(ChoiceField)
... django.forms.models.ModelChoiceField
queryset, # 查询数据库中的数据
empty_label="---------", # 默认空显示内容
to_field_name=None, # HTML中value的值对应的字段
limit_choices_to=None # ModelForm中对queryset二次筛选
ModelMultipleChoiceField(ModelChoiceField)
... django.forms.models.ModelMultipleChoiceField
TypedChoiceField(ChoiceField)
coerce = lambda val: val 对选中的值进行一次转换
empty_value= '''' 空值的默认值
MultipleChoiceField(ChoiceField)
...
TypedMultipleChoiceField(MultipleChoiceField)
coerce = lambda val: val 对选中的每一个值进行一次转换
empty_value= '''' 空值的默认值
ComboField(Field)
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
MultiValueField(Field)
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
SplitDateTimeField(MultiValueField)
input_date_formats=None, 格式列表:[''%Y--%m--%d'', ''%m%d/%Y'', ''%m/%d/%y'']
input_time_formats=None 格式列表:[''%H:%M:%S'', ''%H:%M:%S.%f'', ''%H:%M'']
FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
required=True,
widget=None,
label=None,
initial=None,
help_text=''''
GenericIPAddressField
protocol=''both'', both,ipv4,ipv6支持的IP格式
unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
SlugField(CharField) 数字,字母,下划线,减号(连字符)
...
UUIDField(CharField) uuid类型
...
二、局部钩子和全局钩子
1、定义Form类


from django import forms
from django.core.exceptions import ValidationError #导包 from django.core.exceptions import ValidationError
from app01 import models
class EmpForm(forms.Form):
name = forms.CharField(min_length=5, label="姓名", error_messages={"required": "该字段不能为空!",
"min_length": "用户名太短。"})
age = forms.IntegerField(label="年龄")
salary = forms.DecimalField(max_digits=5, decimal_places=2, label="工资")
r_salary = forms.DecimalField(max_digits=5, decimal_places=2, label="请确认工资")
# 局部钩子
def clean_name(self): # 名称不能随便起,必须要clean_类属性名称
val = self.cleaned_data.get("name")
if val.isdigit():
raise ValidationError("用户名不能全是数字。")
elif models.Emp.objects.filter(name=val):
raise ValidationError("用户名已存在。")
else:
return val
def clean(self):
salary = self.cleaned_data.get("salary")
r_salary = self.cleaned_data.get("r_salary")
if salary != r_salary:
raise ValidationError("工资输入有误。")
else:
return self.cleaned_data
from django import forms
from django.core.exceptions import ValidationError
from app01 import models
class EmpForm(forms.Form):
name = forms.CharField(min_length=5, label="姓名", error_messages={"required": "该字段不能为空!",
"min_length": "用户名太短。"})
age = forms.IntegerField(label="年龄")
salary = forms.DecimalField(max_digits=5, decimal_places=2, label="工资")
r_salary = forms.DecimalField(max_digits=5, decimal_places=2, label="请再输入工资")
def clean_name(self): # 局部钩子
val = self.cleaned_data.get("name")
if val.isdigit():
raise ValidationError("用户名不能是纯数字")
elif models.Emp.objects.filter(name=val):
raise ValidationError("用户名已存在!")
else:
return val
def clean(self): # 全局钩子 确认两次输入的工资是否一致。
val = self.cleaned_data.get("salary")
r_val = self.cleaned_data.get("r_salary")
if val == r_val:
return self.cleaned_data
else:
raise ValidationError("请确认工资是否一致。")
2、视图函数


from django.shortcuts import render,redirect
from app01 import models
# Create your views here.
from django.http.response import HttpResponse
def index(request):
return HttpResponse("okok!")
from app01.MyForms import EmpForm
def add_emp(request):
if request.method == "POST":
# data = request.POST.get()
form = EmpForm(request.POST)
if form.is_valid():
print(1111,form.cleaned_data)
models.Emp.objects.create(**form.cleaned_data)
else:
print(form.errors)#<ul><li>name<ul><li>用户名太短。</li></ul></li><li>__all__<ul><li>工资输入有误。</li></ul></li></ul>
clear_errors = form.errors.get("__all__") # 获取全局钩子错误信息
return render(request, "add_emp.html", {"form": form, "clear_errors": clear_errors})
else:
form = EmpForm()
return render(request, "add_emp.html", {"form": form})
''''''源码解析 MyForms.py:from django import forms 《--- views.py:from app01.MyForms import EmpForm
第一步:form = EmpForm() 生成一个EmpForm表单类对象(该类继承了from django import forms里面的forms.Form类)
第二步:POST请求接受客户的请求数据 进行验证 form = EmpForm(request.POST)---》form.is_valid()
1、
def is_valid(self):
"""Return True if the form has no errors, or False otherwise."""
return self.is_bound and not self.errors
2、 @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
3、
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()
4、MyForms.py有自定义的局部钩子 就会调用 MyForms.py的局部钩子
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)
5、MyForms.py有自定义的全局钩子 就会调用 MyForms.py的全局钩子
def _clean_form(self):
try:
cleaned_data = self.clean()
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
''''''
def add_emp(request):
if request.method == "GET":
form = EmpForm() # 初始化form对象
return render(request, "add_emp.html", {"form":form})
else:
form = EmpForm(request.POST) # 将数据传给form对象
if form.is_valid(): # 进行校验
data = form.cleaned_data
data.pop("r_salary")
models.Emp.objects.create(**data)
return redirect("/index/")
else: # 校验失败
clear_errors = form.errors.get("__all__") # 获取全局钩子错误信息
return render(request, "add_emp.html", {"form": form, "clear_errors": clear_errors})
3、模板文件


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post" novalidate>
{% csrf_token %}
<div>
<label for="id_{{ form.name.name }}">姓名</label>
{{ form.name }} <span>{{ form.name.errors.0 }}</span>
</div>
<div>
<label for="id_{{ form.age.name }}">年龄</label>
{{ form.age }} <span>{{ form.age.errors.0 }}</span>
</div>
<div>
<label for="id_salary">工资</label>
{{ form.salary }} <span>{{ form.salary.errors.0 }}{{ clear_errors.0 }}</span>
</div>
<div>
<label for="id_r_salary">请再输入工资</label>
{{ form.r_salary }} <span>{{ form.r_salary.errors.0 }}{{ clear_errors.0 }}</span>
</div>
<input type="submit">
</form>
</body>
</html>
另一种方法导入app01/models模型类的数据,其它方法与上面一致
12 Django组件-forms组件
forms组件
校验字段功能
针对一个实例:注册用户讲解。
模型:models.py
class UserInfo(models.Model):
name=models.CharField(max_length=32)
pwd=models.CharField(max_length=32)
email=models.EmailField()
tel=models.CharField(max_length=32)
模板: register.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<div>
<label for="user">用户名</label>
<p><input type="text" name="name" id="name"></p>
</div>
<div>
<label for="pwd">密码</label>
<p><input type="password" name="pwd" id="pwd"></p>
</div>
<div>
<label for="r_pwd">确认密码</label>
<p><input type="password" name="r_pwd" id="r_pwd"></p>
</div>
<div>
<label for="email">邮箱</label>
<p><input type="text" name="email" id="email"></p>
</div>
<input type="submit">
</form>
</body>
</html>
视图函数:register
# forms组件
from django.forms import widgets
wid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})
class UserForm(forms.Form):
name=forms.CharField(max_length=32,
widget=wid_01
)
pwd=forms.CharField(max_length=32,widget=wid_02)
r_pwd=forms.CharField(max_length=32,widget=wid_02)
email=forms.EmailField(widget=wid_01)
tel=forms.CharField(max_length=32,widget=wid_01)
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]}
print(form.errors.get("name")) # ErrorList ["错误信息",]
return HttpResponse("OK")
form=UserForm()
return render(request,"register.html",locals())
渲染标签功能
渲染方式1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
<div class="row">
<div class="col-md-6 col-lg-offset-3">
<form action="" method="post">
{% csrf_token %}
<div>
<label for="">用户名</label>
{{ form.name }}
</div>
<div>
<label for="">密码</label>
{{ form.pwd }}
</div>
<div>
<label for="">确认密码</label>
{{ form.r_pwd }}
</div>
<div>
<label for=""> 邮箱</label>
{{ form.email }}
</div>
<input type="submit" class="btn btn-default pull-right">
</form>
</div>
</div>
</div>
</body>
</html>
渲染方式2
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<input type="submit" class="btn btn-default pull-right">
</form>
渲染方式3
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-default pull-right">
</form>
显示错误与重置输入信息功能
视图
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]}
print(form.errors.get("name")) # ErrorList ["错误信息",]
return render(request,"register.html",locals())
form=UserForm()
return render(request,"register.html",locals())
模板
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }} <span>{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit" class="btn btn-default">
</form>
局部钩子与全局钩子
模板
# forms组件
from django.forms import widgets
wid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})
from django.core.exceptions import ValidationError
class UserForm(forms.Form):
name=forms.CharField(max_length=32,
widget=wid_01
)
pwd=forms.CharField(max_length=32,widget=wid_02)
r_pwd=forms.CharField(max_length=32,widget=wid_02)
email=forms.EmailField(widget=wid_01)
tel=forms.CharField(max_length=32,widget=wid_01)
# 局部钩子
def clean_name(self):
val=self.cleaned_data.get("name")
if not val.isdigit():
return val
else:
raise ValidationError("用户名不能是纯数字!")
# 全局钩子
def clean(self):
pwd=self.cleaned_data.get("pwd")
r_pwd=self.cleaned_data.get("r_pwd")
if pwd==r_pwd:
return self.cleaned_data
else:
raise ValidationError(''两次密码不一致!'')
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
clean_error=form.errors.get("__all__")
return render(request,"register.html",locals())
form=UserForm()
return render(request,"register.html",locals())
视图
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }}
<span class="pull-right" style="color: red">
{% if field.label == ''R pwd'' %}
<span>{{ clean_error.0 }}</span>
{% endif %}
{{ field.errors.0 }}
</span>
</div>
{% endfor %}
<input type="submit" class="btn btn-default">
</form>
12,Django组件-forms组件
forms组件
校验字段功能
针对一个实例:注册用户讲解。
模型:models.py
class UserInfo(models.Model):
name=models.CharField(max_length=32)
pwd=models.CharField(max_length=32)
email=models.EmailField()
tel=models.CharField(max_length=32)
模板: register.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<div>
<label for="user">用户名</label>
<p><input type="text" name="name" id="name"></p>
</div>
<div>
<label for="pwd">密码</label>
<p><input type="password" name="pwd" id="pwd"></p>
</div>
<div>
<label for="r_pwd">确认密码</label>
<p><input type="password" name="r_pwd" id="r_pwd"></p>
</div>
<div>
<label for="email">邮箱</label>
<p><input type="text" name="email" id="email"></p>
</div>
<input type="submit">
</form>
</body>
</html>

视图函数:register

# forms组件
from django.forms import widgets
wid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})
class UserForm(forms.Form):
name=forms.CharField(max_length=32,
widget=wid_01
)
pwd=forms.CharField(max_length=32,widget=wid_02)
r_pwd=forms.CharField(max_length=32,widget=wid_02)
email=forms.EmailField(widget=wid_01)
tel=forms.CharField(max_length=32,widget=wid_01)
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]}
print(form.errors.get("name")) # ErrorList ["错误信息",]
return HttpResponse("OK")
form=UserForm()
return render(request,"register.html",locals())

渲染标签功能
渲染方式1

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h3>注册页面</h3>
<div>
<div>
<div>
<form action="" method="post">
{% csrf_token %}
<div>
<label for="">用户名</label>
{{ form.name }}
</div>
<div>
<label for="">密码</label>
{{ form.pwd }}
</div>
<div>
<label for="">确认密码</label>
{{ form.r_pwd }}
</div>
<div>
<label for=""> 邮箱</label>
{{ form.email }}
</div>
<input type="submit">
</form>
</div>
</div>
</div>
</body>
</html>

渲染方式2

<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<input type="submit">
</form>

渲染方式3

<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>

显示错误与重置输入信息功能
视图

def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
print(form.cleaned_data) #
print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]}
print(form.errors.get("name")) # ErrorList ["错误信息",]
return render(request,"register.html",locals())
form=UserForm()
return render(request,"register.html",locals())

模板

<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }} <span>{{ field.errors.0 }}</span>
</div>
{% endfor %}
<input type="submit">
</form>

局部钩子与全局钩子
模板

# forms组件
from django.forms import widgets
wid_01=widgets.TextInput(attrs={"class":"form-control"})
wid_02=widgets.PasswordInput(attrs={"class":"form-control"})
from django.core.exceptions import ValidationError
class UserForm(forms.Form):
name=forms.CharField(max_length=32,
widget=wid_01
)
pwd=forms.CharField(max_length=32,widget=wid_02)
r_pwd=forms.CharField(max_length=32,widget=wid_02)
email=forms.EmailField(widget=wid_01)
tel=forms.CharField(max_length=32,widget=wid_01)
# 局部钩子
def clean_name(self):
val=self.cleaned_data.get("name")
if not val.isdigit():
return val
else:
raise ValidationError("用户名不能是纯数字!")
# 全局钩子
def clean(self):
pwd=self.cleaned_data.get("pwd")
r_pwd=self.cleaned_data.get("r_pwd")
if pwd==r_pwd:
return self.cleaned_data
else:
raise ValidationError(''两次密码不一致!'')
def register(request):
if request.method=="POST":
form=UserForm(request.POST)
if form.is_valid():
print(form.cleaned_data) # 所有干净的字段以及对应的值
else:
clean_error=form.errors.get("__all__")
return render(request,"register.html",locals())
form=UserForm()
return render(request,"register.html",locals())

视图

<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div>
<label for="">{{ field.label }}</label>
{{ field }}
<span>
{% if field.label == ''R pwd'' %}
<span>{{ clean_error.0 }}</span>
{% endif %}
{{ field.errors.0 }}
</span>
</div>
{% endfor %}
<input type="submit">
</form>

关于forms组件和winform 组件的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于03、 forms组件、08 Django组件-Forms组件、12 Django组件-forms组件、12,Django组件-forms组件的相关信息,请在本站寻找。
本文标签: