文章目录
一、form组件
通常情况下,我们需要自己手动在HTML页面中,编写form标签和其内的其它元素。但这费时费力,而且容易出错,数据验证也比较麻烦。
因此,Django在内部集成了一个表单模块,专门用来帮助我们快速处理表单相关的内容。Django的表单模块给我们提供了下面三个主要功能:
- 准备和重组数据,用于页面渲染;
- 为数据创建HTML表单元素;
- 接收和处理用户从客户端提交的表单及数据。
二、form表单的基本语法
编写Django的form表单,非常类似我们在模型系统里编写一个模型。在模型中,一个字段代表数据表的一列,而form表单中的一个字段代表<form>
中的一个<input>
元素。
1. form表单类的书写
在app文件夹下,新建一个forms.py
文件写入:
from django import forms
class MyForm(forms.Form):
# 最多不能超过20个字符,最少5个字符
username = forms.CharField(max_length=20, min_length=5)
password = forms.CharField(max_length=20, min_length=5)
# 会验证输入是否符合邮箱格式
email = forms.EmailField()
Form类中的每个字段不仅会验证数据,还会将数据转换为合适的格式。比如,DateField
类型的字段会将收到的数据转换为python的datetime.data
对象,无论我们传给它的是对象还是字符串,只要是有效的,它都会转换为datetime.data
对象。
2. 校验数据
在终端中运行命令python manage.py shell
,导入表单类,就可以在终端中进行以下步骤。
-
创建表单对象:
实例化表单类,参数为一个字典,键为类属性,值为需要提交的数据。form_obj1 = MyForm({'username':'mahugh','password':'123456','email':'123456'}) # email的值不符合邮箱格式 form_obj2 = MyForm({'username':'mahugh','password':'123456','email':'123456@qq.com'}) # 所有值都符合条件
-
查看验证是否通过:
is_valid()
方法,只要有一个键值对不符合验证条件,就返回false。传入多余的键值对,不会影响验证结果,但少传键值对就会返回false。
form_obj1.is_valid() # 返回:False form_obj2.is_valid() # 返回:True
-
查看通过验证的数据:
cleaned_data
只包含合法的(符合验证条件的)数据,多出的和不合法(不符合验证条件的)都不会被包含。form_obj1.cleaned_data # 返回:{'username': 'mahugh', 'password': '123456'} form_obj2.cleaned_data # 返回:{'username': 'mahugh', 'password': '123456', 'email': '123456@qq.com'}
未调用
is_valid()
方法进行验证的form表单对象,调用cleaned_data
属性查看正确的数据时,会报错:'xxx' object has no attribute 'form_true'
。 -
查看未通过验证的错误信息:
errors
只包含不合法数据的错误信息,多出的和合法的都不会被包含。form_obj1.errors # 返回:<ul class="errorlist"><li>email<ul class="errorlist"><li>输入一个有效的 Email 地址。</li></ul></li></ul>
errors
会被渲染成无序列表。
3. 渲染表单标签
视图中,生成一个表单对象,传递给模板:
def index(request):
form_obj = MyForm(auto_id=True)
return render(request, 'index.html', {'form_obj':form_obj})
如果 auto_id
设置为 True,那么表单输出将包括 <label>
标签,并将使用字段名作为每个表单字段的 id属性值,否则,不输出 <label>
标签,不设置id属性。
在模板文件中,写入变量:
<form action="" method="post">
{{ form_obj }}
</form>
这样就会将所有表单字段渲染为input框,并渲染label提示信息。提示信息默认为首字母大写的字段名称。但这种方法,过于死板,所以不常用。先看一些高级语法,之后总结常用写法。
高级语法:
-
只渲染指定表单字段为input框,不渲染提示信息:
{{ form_obj.username }}
或者,使用循环渲染所有字段为input框:
{% for obj in form_obj %} <!-- obj渲染出来只是一个个的input框 --> {{ obj }} {% endfor %}
-
只渲染指定字段的提示信息,不渲染input框:
{{ form_obj.username.label }}
如果想自定义提示信息,可以在表单类中,传入label参数:
class MyForm(forms.Form): username = forms.CharField(max_length=20, min_length=5, label='姓名')
这样,提示信息就会变成“姓名”了,而不是“Username”。
-
使用指定的标签,包裹住字段被渲染后的html代码:
<!-- 比如,以p标签包裹username字段 --> {{ form_obj.username.as_p }}
最终渲染的html代码如下:
<p> <label for="id_username">姓名</label> <input type="text" name="username" maxlength="20" minlength="5" required="" id="id_username"> </p>
django支持的标签还有:
<!-- 渲染为无序列表,以li标签进行包裹 --> {{ form_obj.as_ul }} <!-- 渲染为表格,必须放在table标签内 --> {{ form_obj.as_table }}
三、展示错误信息
测试准备:由于浏览器自带一些简单且容易跳过的验证措施,会干扰我们后端的测试验证,所以先在form标签中设置
novalidate
属性,来跳过浏览器验证。
1. 展示错误信息
-
首先在视图中使用
is_valid()
方法,验证后端提交的数据是否合法。合法就做后续步骤,不合法就将页面重新渲染一遍:def login_view(self, request): # request.POST可以看作是一个字典,因此可以直接用来实例化表单对象 form_obj = MyForm(request.POS, auto_id=True) if form_obj.is_valid(): return HttpResponse('好耶!验证通过了!') else: # 带着错误信息,再渲染一遍 return render(request, 'index.html', context={'form_obj': form_obj})
不要忘记
auto_id=True
参数! -
然后在模板的合适位置,写入错误信息变量:
<form action="" method="post" novalidate> {% for form_obj in reg_form %} <label for="{{ form_obj.auto_id }}">{{ form_obj.label }}:</label> {{ form_obj }} {% if form_obj.errors %} {% for error in form_obj.errors %} <div>{{ error }}</div> {% endfor %} {% endif %} {% endfor %}
{{ form_obj.auto_id }}
返回字段input框的id属性值,前提是实例化form表单对象时,写了auto_id=True
参数。
因为{{ form_obj.errors }}
默认是一个无序列表,使用for循环可以更好的展示多条错误信息。
也可以写作{{ form_obj.errors.0 }}
,只获取错误信息的字符串,不显示 • 符号。
2. 自定义错误信息
在表单类的字段中,传入error_messages
参数,该参数接收一个字典:
class MyForm(forms.Form):
username = forms.CharField(max_length=20, min_length=5,
error_messages={
'max_length': '最多不能超过20个字符',
'min_length': '最少不能少于5个字符',
'required': '这个东西必须给我回答,不然揍你!'
})
# 邮箱比较特殊,需要单独记忆
email = forms.EmailField(label='邮箱', error_messages={
'invalid': '这™是邮箱?'
})
字典当中的键称为“错误信息键”,值为验证失败时提示的错误信息。
required
错误信息键,指在设置了required=True
时(表示该字段必须填写),没有填写信息,就会提示该键后面的提示信息。
invalid
则会在字段自带的验证未通过时,提示后面的信息,比如在上面的例子中,如果填写的不是一个邮箱,则会提示:“这™是邮箱?”。
不同的字段,拥有不同的错误信息键。常用的错误信息键会在字段 参数和类型 中讲到。
3. 动态添加错误信息
add_error()
方法向表单特定字段添加错误信息。
form_obj.add_error('字段名', 错误信息)
字段的名称如果为None,error将作为Form.non_field_errors()
的一个非字段错误。
错误信息可以是一个字符串,或者最好是 ValidationError 的实例,如:ValidationError("用户名已存在")
。
注意:Form.add_error()
会自动从 cleaned_data 中删除相关字段。
四、form表单的钩子函数(HOOK)
钩子函数是一类有特殊用法的函数,它会在特定节点自动触发完成响应操作。分为局部钩子函数和全局钩子函数两类。
1. 局部钩子(只涉及到一个字段)
局部钩子函数的函数名就是clean_字段名
,你要给那个字段自定义规则,这个字段名就是谁!
比如:在django表单进行验证时,检查用户名是否含有违规字词(只涉及到username字段):
from django import forms
from django.core.exceptions import ValidationError
from app01.models import UserInfo
class MyForm(forms.Form):
# 表单字段
username = forms.CharField(label='用户名', max_length=12, min_length=2)
# 局部钩子
def clean_name(self):
# 获取用户输入的用户名
val = self.cleaned_data.get('username')
# 查看数据库中是否有这个值
ret = UserInfo.objects.filter(username=val)
# 如果在数据库中没有,就通过匹配
if not ret:
return val
# 否则,说明在数据库中有该用户名
else:
raise ValidationError("用户名已存在")
2. 全局钩子(涉及到多个字段)
全局钩子的函数名必须是clean()
!
比如:在django表单进行验证时,检查两次用户密码是否一致(涉及到password和confirm_password两个字段):
from django import forms
from django.core.exceptions import ValidationError
from app01.models import UserInfo
class MyForm(forms.Form):
# 表单字段
password = forms.CharField(label='密码', max_length=18, min_length=6)
confirm_password = forms.CharField(label='确认密码', max_length=18, min_length=6)
# 全局钩子
def clean(self):
pwd=self.cleaned_data.get("password")
r_pwd=self.cleaned_data.get("confirm_password")
# 上面两个,有可能自带规则或自定义规则未通过,则get取值是空
if pwd and r_pwd: # 如果两个都通过了第一层说明clean_data中有值就是true
if pwd==r_pwd:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致")
else:
'''
如果两个只要有其中一个不在clean_data里,那就没必要在比较了,
因为本身就已经在errors里了
'''
return self.cleaned_data
五、字段参数和类型
1. 核心参数
-
label
:用来自定义表单的提示信息(label标签的文本)。 -
error_messages
:用于自定义验证不通过时的提示信息,在上面的自定义错误信息一节中已经讲过。 -
initial
:用来给字段设置默认值,对应input标签的value属性。 -
required
:指定该字段是否是必须填写的,默认值为true
,表示该字段必须填写,否则不能通过验证。
widget
参数
为字段指定一个Widget类,它有以下作用:
-
指定部件,对应的是input的type属性:
widget=forms.TextInput # 普通文本类型 widget=forms.PasswordInput # 密码类型 widget=forms.EmailInput # 邮箱类型
较为复杂的部件:
-
单选按钮
RadioSelect
(对应radio):gender = forms.fields.ChoiceField( choices=((1, "男"), (2, "女"), (3, "保密")), label="性别", initial=3, widget=forms.RadioSelect() )
-
选择框
CheckboxInput
(对应checkbox):# 单选框 keep = forms.ChoiceField( label="是否记住密码", initial="checked", widget=forms.CheckboxInput() ) # 多选框 hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), label="爱好", initial=[1, 3], widget=forms.CheckboxSelectMultiple() )
-
下拉框
CheckboxInput
(对应option):# 单选下拉框 hobby = forms.ChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=3, widget=forms.Select() ) # 多选下拉框 hobby = forms.MultipleChoiceField( choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), label="爱好", initial=[1, 3], widget=forms.SelectMultiple() )
更多内置部件参考官方文档:传送门
-
-
传入参数,多用于设置标签的属性值:
比如设置class
属性,使用attrs
参数:widget=forms.TextInput( attrs={'class': 'c1 c2 c3'} )
validators
参数
给字段提供一个验证函数列表,用于自定义验证规则:
from django.core.exceptions import ValidationError
# 这个东西是用来翻译东西的
from django.utils.translation import gettext_lazy as _
def check_username(value):
# 阿巴阿巴,验证操作……
if 阿巴阿巴:
# 验证不通过,抛出异常
raise ValidationError(
# 错误信息,翻译用不用看你自己
_('%(value)这玩意得这个样子才行'),
params={'value': value},
)
class MyForm():
username = forms.CharField(validators=[check_username])
之后,username
字段就会使用check_username
函数进行验证。django还自带了一些验证器,比如RegexValidator
验证器可以帮助我们通过正则表达式,快速书写验证方法:
validators=[
# 参数为正则表达式和错误信息
RegexValidator(r'我是正则表达式', '你这玩意不符合我这个正则表达式啊')
]
更多内置验证器参考官方文档:传送门
其他参数
help_text=''
: 帮助信息(在标签旁边显示)localize=False
:是否支持本地化disabled=False
:是否可以编辑label_suffix=None
:Label内容后缀
2. 常用类型
-
BooleanField
:- 默认部件:
CheckboxInput
- 空值:
False
- 规范化为:Python 的
True
或False
值。 - 如果字段有
required=True
,则验证该值是否为True
(例如,复选框被选中)。 - 错误信息键:
required
- 默认部件:
-
CharField
:-
默认部件:
TextInput
-
空值:
empty_value
的值。 -
规范化为:一个字符串。
-
如果提供了
max_length
和min_length
,则验证输入的字符数。否则,所有输入都有效。 -
错误信息键:
required
、max_length
、min_length
。 -
可选参数:
max_length
:最大字符数。min_length
:最小字符数。strip
:如果True
(默认),该值将被去掉前面和末尾的空白。empty_value
:用来表示“空”的值。默认为空字符串。
-
-
IntegerField
:当
Field.localize
为False
时是NumberInput
否则,该字段的默认表单部件是TextInput
否则,该字段的默认表单部件是TextInput
。-
空值:
None
-
规范化为:Python 的整数。
-
验证给定值是否为整数。
-
错误信息键:
required
、invalid
、max_value
、min_value
。 -
可选参数:
max_value
:最大值。min_value
:最小值。
-
-
DecimalField
:当
Field.localize
为False
时是NumberInput
否则,该字段的默认表单部件是TextInput
。-
空值:
None
-
规范化为:Python 的
decimal
。 -
验证给定值是否为十进制。如果提供了
max_value
和min_value
,则额外验证值的范围。前面和后面的空格会被忽略。 -
错误信息键:
required
、invalid
、max_value
、min_value
、max_digits
、max_decimal_places
、max_whole_digits
。 -
可选的参数:
max_value=None
:最大值。
min_value=None
:最小值。max_digits
:最大数字总位数。decimal_places
:小数位数。
-
-
DateField
:-
默认部件:
DateInput
-
空值:
None
-
规范化为:Python 的
datetime.date
对象。 -
验证给定值是
datetime.date
、datetime.datetime
或以特定日期格式化的字符串。 -
格式:“2015-09-01”
-
错误信息键:
required
、invalid
。 -
可选的参数:
input_formats
:用于将字符串转换为有效的datetime.date
对象的格式列表,使用的是time模块的那一套格式化方法。
-
-
TimeField
:与
DateField
基本相同,只有格式不同:“11:12” -
DateTimeField
:与
DateField
基本相同,只有格式不同:“2015-09-01 11:12” -
EmailField
:-
默认部件:
EmailInput
-
空值:empty_value` 的值。
-
规范化为:一个字符串。
-
使用
EmailValidator
来验证给定的值是一个有效的电子邮件地址,使用一个适度复杂的正则表达式。 -
错误信息键:
required
、invalid
:输入内容不符合邮箱格式时提示的错误信息。 -
可选参数:
max_length
、min_length
和empty_value
,它们的工作原理与CharField
一样。
-
-
FileField
:-
默认部件:
ClearableFileInput
-
空值:
None
-
规范化为:一个
UploadedFile
对象,它将文件内容和文件名包装成一个单一对象。 -
可以验证非空文件数据已经绑定到表单中。
-
错误信息键:
required
、invalid
、missing
、empty
、max_length
。 -
可选的验证参数:
max_length
:文件名的最大长度。allow_empty_file
:是否允许文件内容为空。
更多表单字段类型参考官方文档:传送门
-