Form组件
在之前编写的项目中,表单这一类内容,都由我们手工编写.然后将数据提交到后端,后端程序从请求中对每个数据进行验证.
从抽象的过程来看,表单的每一个提交的数据,都有一种默认的类型,对应着数据库中的某种字段,又都对应着一些默认的HTTP代码.比如一组多选,在后端通过getlist取得的是一个列表.在页面上展示的默认代码是一组多选checkbox.
Django的Form组件是这样一个对象:根据表单要提交的数据,自动生成HTML代码,然后将对应的模板标签放置在页面上,后端能够操作这个对象,根据取回来的数据预先校验然后返回校验结果,则建立表单和校验的对应关系和编写代码的工作量会减少很多.
Django 1.11的官方文档对于Form组件,提供了
在Django中使用Form
和
Form API 清单
两部分文档.
Form组件初步使用
通过一个之前经常使用的登陆用户名的表单来进行简单的Form操作:
<form action="/add_name/" method="post">
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" value="{{ current_name }}">
<input type="submit" value="OK">
</form>
这个表单很简单,就是一个输入用户名并且提交的表单.可以看到几个结构化的特点:form标签规定了提交的URL和请求类型.label标签和input标签成对出现,input标签由于是输入用户名,类型是text,还有值的设置.一个表单还一定会有一个功能是submit的元素,这里是input标签作为submit元素.
现在这里的所有代码,都不在通过HTML编码,而是采取在后端生成一个Form对象,用Form对象生成的HTML代码来覆盖这个位置的代码,同时通过Form对象的操作来验证数据:
在views.py或者其他地方,编写如下代码:
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
Form对象必须继承forms.Form类,有点像ORM的表类一样.然后在其中定义了一个属性,看起来也很像ORM定义表字段,这个叫your_name的属性对应的是一个CharField类,然后设置了具体属性label="Your_name",max_length=100
.
即使此时还不了解Form类,我们也可以猜测到,label属性对应的值就是HTML页面里label属性的text内容.max_length则是验证条件之一,即最长长度是100个字符.
猜测其实是正确的.除此之外,每个Form对象有一个is_valid()方法,当调用这个方法的时候,如果所有填入的内容都是有效的,则这个方法会返回True,然后会将表单内所有的数据放到Form对象的cleaned_data
属性中.
接下来,需要先继续编写完后端代码,将这段东西传递到页面上,然后再修改模板看一下效果.
def get_name(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
return HttpResponse('thanks')
else:
form = NameForm()
return render(request, 'index.html', {'form': form})
视图的逻辑很简单,如果是POST请求,就通过刚才建立的类,把POST请求传进去,利用POST请求附带的所有数据实例化一个Form类,然后对这个类调用验证方法来验证,如果通过验证,就返回成功的消息.如果是GET请求,就返回空白表单供填写.
最后是编写模板,由于我们使用了表单对象,因此就将模板修改成:
<form action="/add_name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
最后运行Django站点,会发现{{ form }}
的部分在实际的页面中显示如下:
<label for="id_your_name">Your name:</label>
<input type="text" name="your_name" maxlength="100" required="" id="id_your_name">
在实际的输入框中,会发现也只能输入最长100个字符.实际上Form对象做的事情就是生成了对应的HTML代码然后在后台进行了控制.对于这个例子来说,由于控制了输入长度和不能为空,所以不会出现is_valid()为False的情况.如果print(form.clean_data)
,则可以看到传入的输入框name和对应的值组成的字典.
Form组件的组成内容
可以看到,通过一个Form对象,就可以将页面上的表单元素和后端通过这个对象连接起来,取数和验证都变得更加方便,实际上form对象的方法和属性还有很多,可以单独控制页面元素并且显示出错信息.
首先来看一下如何初始化Form对象.
从前边例子可以看出来,Form的类定义有点类似ORM的类.Form对象里也有各种Field,也就是各种表单元素.Django 1.11 的Fields 官方文档.
Form对象的写法示例:
class LoginForm(forms.Form):
pwd = forms.CharField(
min_length=6,
label="密码",
widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
可以看到,每一个属性都要对应forms.fields.XXX的某一类fields,然后其中有参数 field arguments.参数主要是用来控制验证条件,参数中还有一个特殊的参数是widget,用于控制HTML代码生成.
Fields
先来看一下常用的fields:
CheckboxInput
required
所有field的子类默认都设置了required=True.而布尔字段没有选中意味着False,会触发requird错误.因此在布尔字段上要特别设置required=False
CharField
TextInput
required, max_length, min_length
得到一个unicode字符串,如果设置最大和最小长度,在HTML内就会验证.如果不设置最大和最小,任何输入都可以通过验证.还有参数strip,默认为True,表示去除输入前后空格.
ChoiceField
Select
required, invalid_choice
用于单选的字段,更改默认的widget时候必须注意搭配,choices的参数必须是可迭代的序列,每一个元素是一个2个元素的元组,第一个元素是值,第二个是显示的内容.
DateField
DateInput
required, invalid
返回一个Python的 datetime.date 对象,HTML表现形式是一个日期输入框.可以用input_formats字符串格式化参数指定具体样式
DateTimeField
DateTimeInput
required, invalid
与DateField类似.也有input_formats字符串格式化参数
DecimalField
NumberInput或TextInput
required, invalid, max_value, min_value, max_digits, max_decimal_places, max_whole_digits
十进制浮点数字段,返回Python的decimal对象,可选参数是最大值,最小值,最大位数,最大小数位数
DurationField
TextInput
required, invalid
返回一个Python timedelta对象,表示间隔.
EmailField
EmailInput
required, invalid
返回unicode字符串的邮件地址.可选参数是 max_length min_length.
FileField
ClearableFileInput
required, invalid, missing, empty, max_length
上传文件.返回一个Uploadfile对象,包含文件名和文件内容.两个可选参数max_length和allow_empty_file.上传文件的时候还需要对form元素进行设置.
FilePathField
Select
required, invalid, max_value, min_value
选择文件上传,有一个必须参数path来指定想要开始选择的目录.具体看这里
FloatField
NumberInput或TextInput
required, invalid
可选参数为max_value 和 min_value,控制最大和最小值.
ImageField
ClearableFileInput
required, invalid, missing, empty, invalid_image
与上传文件类似,但使用ImageField需要安装pillow库.
IntegerField
NumberInput或TextInput
required, invalid, max_value, min_value
可选参数是max_value 和 min_value
MultipleChoiceField
SelectMultiple
required, invalid_choice, invalid_list
使用choices属性传入选择项.用于多选.更改默认对应的widget时候注意搭配
Fields arguments
再来看一下Core field arguments 核心字段属性,这些Core field arguments是建立Form对象里的fields时一定要包含的属性.除此之外,上边的fields表格也列出了某些field可以额外添加的参数=属性.
Field.initial
设置初始化的值,也就是设置标签的value属性.注意,不同的field,initial需要被设置成对应的对象,比如时间字段就必须用datetime系列对象赋值给initial属性
Field.widget
设置对应的widget类,用于控制具体的HTML代码
Field.help_text
在HTML中显示帮助文本信息
Field.error_messages
用于覆盖默认的错误信息,需要采用error_messages={‘required’: ‘Please enter your name’}类似的方法来传入,前边的键就是错误键的名称,值是自定义的错误信息.
Field.validators
选择针对该字段的验证器,验证器的详细看这里
Field.localize
和本地化有关,控制结果的本地化输出.
Field.disabled
设置表单元素的属性是否为disabled
Field.has_changed()
检测元素的值是否从initial值发生了变化,返回布尔类型.
Widgets 插件
最后一部分在建立form对象时候要了解的是widget,官方文档的原话是: A widget is Django’s representation of an HTML input element.也就是说一个插件就对应着一段HTML代码.
通过fields可以知道要拿到哪一种数据类型,通过fields arguments 可以得到验证相关的条件,widget则是最后一步,即将字段的逻辑通过HTML展示出来.同时widget也有各种属性可以设置,用于更好的控制具体HTML代码.
所有的widget类都继承自 Widget 和 MultiWidget 两个类,其中Widget有attrs属性,用来设置HTML标签的各种属性,常用的是设置css类从而应用样式.
内建的Widget类
Form.is_bound
如果没有任何数据传入而新建Form对象,这是一个没有绑定的Form对象,如果传入了数据比如request.POST,这就是一个绑定了一个具体表单的数据,这个方法返回Form对象是否是一个绑定的对象
Form.clean()
使用clean方法意味着调用is_valid()方法然后返回一个布尔值
Form.errors
返回错误键与错误内容的字典.调用该属性和is_valid()方法都会触发对Form对象的校验.
Form.errors.as_data()
错误键不变,值变成原始的错误对象
Form.errors.as_json(escape_html=False)
将错误序列化为JSON对象,可加上 escape_html=True进行转义以便直接在HTML内使用
Form.initial
用字典的形式设置初始值,如果Form对象通过initial属性和字段的initial属性都设置了初始值,以Form对象的优先.
Form.get_initial_for_field(field, field_name)
取得初始值,按照先取Form.initial,再取fields.initial的顺序,如果初始值需要求值也会被求值.
Form.has_changed()
整个表单的初始值是否改变,需要先设置Form的initial属性,然后调用该方法即可查看是否改变.
Form.changed_data
返回一个列表,包含所有与初始值有变化的字段名称.
Form.fields
直接用对象的字段变量名就可以访问该字段.之后再用field的那些arguments就可以访问字段的各种属性
Form.cleaned_data
当is_valid()或其他触发验证的动作实行后,如果通过了验证,则所有的数据会被包含在这个属性对应的一个字典里.而且所有的数据都被整理过,比如从前边可以知道,时间类型默认对应的widget是text类型,但是在cleaned_data中,时间类型的数据会被整理成datetime类型.其他的数据类型可以参考field部分的表格.
Form.as_p()
按照P段落渲染,将所有的表单元素包裹在P标签内.改变的是直接print(Form对象)的结果.
Form.as_ul()
将每一个表单元素放进一个ul的li元素中,影响print结果
Form.as_table()
包裹在tr th标签里,但是table元素需要页面来提供,一般不采用该方法.
Form.label_suffix
这个属性的内容会在渲染的时候追加到所有的label 的text内容之后.
Form.use_required_attribute
这个属性被设置成True的时候,所有必须填写的表单元素标签内都会带有required 的HTML 5 属性.
在模板内使用Form对象
最后一部分,就是看看Form对象如何在HTML中展示. 表单的关键,是展示提示,输入框以及错误信息.
有两种展示方式:
自动渲染就是一次性将整个form按照某种形式渲染出来,不单独操作表单的各个元素.
{{ form.as_table }}
{{ form.as_p }}
{{ form.as_ul }}
如果在对象内不做任何设置,那么元素的id会被自动设置成id_属性名.这种方法可自定义的部分较少,需要后期慢慢配样式.一般采用第二种方法.
手动渲染就是将传入模板的form对象的各个字段和错误信息取出,自行编写.
其基础是 {{ form.name_of_field }} 表示渲染表单中的一个输入元素.
{{ form.name_of_field.label }}表示该字段对应的label标签.
{{ form.name_of_field.errors }}表示经过验证后的该字段对应的错误消息.由于错误信息只会同时有一个,所以一般用{{ form.name_of_field.errors.0 }}取出错误信息.
看一下模板内的操作列表:
Form对象在模板内的操作
tag名称
至此已经学完了Form对象的基础,目前已经可以建立各种类型的表单并且验证来得到信息,同时在页面上展示错误信息.
所以,对于已经学过的内容来说,如果想要快速建立一个表单,就可以采取给form对象的字段指定一些css类和id的方法,然后通过bootstrap来设置样式.在模板中通过手工渲染,分开每一个部件方便展示和修改.
对于表单来说,其实还有一部分比较重要的内容,那就是前边fields arguments中提到的验证器.对于一些特殊需求,采取何种规则验证Form是一个需要特别控制的,并不能完全交给默认的验证机制.
validators参数和自定义验证器
Form对象的建立和HTML展示都已经学习完毕,现在最重要的一个功能就是如何验证.刚刚的Fields arguments有一个参数叫validators
,这个参数指的就是验证器.验证器有很多种类,不同的验证器对应不同的验证规范,还可以自定义验证器.
验证器的工作实际上是对应form arguments的,比如指定了max_length,就意味着这个字段带了一个max_length的验证器.validators
则用来指定自定义的验证器,通常是正则验证器.自定义的验证器额外附加在所有的验证器上.当数据过来的时候,所有的验证器都要通过,这个字段才算没有错误.
自定义验证器
自定义验证器有两种方式,一种是自行编写一个符合验证标准的函数,然后必须返回一个Django的ValidationError对象及错误信息,一种是用内置的验证器对象生成新的验证实例,然后传给validators函数.
# 自定义一个函数,参数是传入的值,然后验证之后返回一个ValidationError.
from django.core.exceptions import ValidationError
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')
然后在某个字段内,传入自定义的验证器给validators参数:
phone = fields.CharField(validators=[mobile_validate, ],
error_messages={'required': '手机不能为空'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': u'手机号码'}))
从传参数的方法可以看出来,是一个验证器列表,说明可以传入多个自定义的验证器.
# 用内置的验证器生成新的实例对象,然后传给validators参数.
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
所有的内置验证器都在django.core.validators
这个模块内,自定义验证器既可以编写函数也可以生成内置验证器的实例对象:
RegexValidator(regex=None, message=None, code=None, inverse_match=None, flags=0)
正则验证器,一般使用前两个参数,传入正则表达式和错误信息,如果验证器未验证通过,就返回错误信息
regex 是正则表达式
message 是错误信息
code是供 ValidationError 使用的错误代码,默认是invalid,一般无需改动
匹配模式,默认是False
用于regex是一个编译过的正则表达式,一般不用
EmailValidator(message=None, code=None, whitelist=None)
邮件验证器,EmailField默认使用该验证器
message是错误信息
code是错误代码
whitelist是白名单,即只有在whitelist参数里的邮件地址才能通过验证
URLValidator(schemes=None, regex=None, message=None, code=None)
URL验证器,URL相关field默认使用该验证器
schemes指URL方案,如果不指定,默认是[‘http’, ‘https’, ‘ftp’, ‘ftps’]也就是只能通过HTTP和FTP协议的URL
regex是正则表达式
message是错误信息
code是错误代码
validate_email
一个 EmailValidator 的不带任何参数的实例
validate_slug
一个正则表达式验证器的实例,仅能验证字母,数字,减号和下划线的组合
validate_unicode_slug
一个正则表达式验证器的实例,仅能验证UNICODE的字母,数字,减号和下划线的组合
validate_ipv4_address
一个正则表达式验证器的实例,验证合法的ipv4地址
validate_ipv6_address
这是一个用了django.utils.ipv6 模块的ipv6地址的验证器
validate_ipv46_address
实际上是同时使用了前边两个验证器的实例
validate_comma_separated_integer_list
一个正则表达式验证器的实例,验证逗号分割的数字
int_list_validator(sep=’, ‘, message=None, code=’invalid’, allow_negative=False)
正则表达式验证器实例,用于检测字符串是否由一串整数被sep参数的分隔符分割后组成
sep表示分隔符
message为错误信息
allow_negative表示是否允许负整数
MaxValueValidator(max_value, message=None)
最大值验证器,max_value参数默认使用该验证器
MinValueValidator(min_value, message=None)
最小值验证器
MaxLengthValidator(max_length, message=None)
最大长度验证器,
MinLengthValidator(min_length, message=None
最小长度验证器
DecimalValidator(max_digits, decimal_places)
Decimal类型验证器
max_digits 是总的最长位数
decimal_places 是小数的位数
还有两个文件验证器可以看这里