相关文章推荐

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 是小数的位数
  • 还有两个文件验证器可以看这里