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:字段名称 | 默认插件 | 错误键 | 解释 |
---|---|---|---|
BooleanField | 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.required | 默认设置为True,表示一定要输入内容,None或者空字符串都会引发错误. |
Field.label | 用于生成HTML代码对应该输入元素的label标签的text内容. |
Field.label_suffix | 用于覆盖整个表单级别的label_suffix,就是给label的text部分加上后缀 |
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类 | |
---|---|
类名 | 解释 |
TextInput | 输入类型是text,渲染的时候按照<input type="text" ...>渲染 |
NumberInput | 输入类型是number,渲染的时候是number类型的input标签 |
EmailInput | 渲染的时候是email类型的input标签 |
URLInput | URL类型的input标签 |
PasswordInput | password类型的input标签,可以带一个额外属性是render_value,表示验证失败之后填写在密码框内的值,默认是False即保留原来的值 |
HiddenInput | 类型是hidden 的input标签 |
DateInput | 类型是text的input标签,可以使用额外参数format来控制格式化 |
DateTimeInput | 类型是text的input标签,同样有format属性 |
TimeInput | 类型是text的input标签,同样有format属性 |
Textarea | 渲染为textarea标签 |
CheckboxInput | 渲染为checkbox对象,有一个调用方法是check_test,检查是否应该选中这个值 |
Select | 渲染为select及内嵌的option标签.有choices属性用于设置各个选项 |
SelectMultiple | 多选,渲染为<select multiple="multiple"> |
RadioSelect | 渲染成一个ul,每个li内部包含一个radio类型的input,模板内的标签使用方法比较多,具体看这里 |
CheckboxSelectMultiple | 渲染成一个ul,每个li内包含一个类型是checkbox的input标签 |
FileInput | 渲染成<input type="file" ...> |
FORM 后端 API
其他次要的可以看官方文档.至此已经学习完了所有建立Form类所需要知道的内容. 下一步是要看Form对象如何使用,使用的地方分为两种,一种是在后端使用,先来看看后端的API:Form API | |
---|---|
属性或方法名 | 解释 |
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 }}
Form对象在模板内的操作 | |
---|---|
tag名称 | 解释 |
{{ field.label }} | 字段的label属性的内容,就是一个字符串 |
{{ field.label_tag }} | 一个完整的label标签,推荐使用该tag与field搭配 |
{{ field.id_for_label }} | 这个字段使用的id |
{{ field.value }} | 字段的值,提交表单之后会动态根据当前值改变 |
{{ field.html_name }} | html的name属性值 |
{{ field.help_text }} | 帮助信息 |
{{ field.errors }} | 当前字段的错误信息,如果验证通过则不会有错误信息.可以对其迭代取出所有错误或者用.0取第一个错误内容 |
{{ field.is_hidden }} | 判断当前字段是否是隐藏的 |
{{ form.hidden_fields }} | 这里注意之前的是field的属性,这里是form的属性,表示表单内的全部hidden字段,可以迭代取出具体字段 |
{{ form.visible_fields }} | 这个是所有的可视字段. |
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 是小数的位数
还有两个文件验证器可以看这里