Django之form组件

一、简介

web框架中避免不了对表单的验证,我们可以通过js在表单数据提交前做一次校验,然而在Django中form组件不仅仅提供验证功能,还可以生成HTML,还可以与model结合使用,等等强大的功能。

先了解下主要的作用:

  • 生成HTML标签
  • 验证用户数据(显示错误信息)
  • HTML Form提交保留上次提交数据
  • 初始化页面显示内容
二、基本操作

1.创建form类

#_*_ coding:utf-8 _*_
#Author:wd
from django.forms import Form
from django.forms import widgets
from django.forms import fields
class myform(Form):
    user=fields.CharField(   #字段名字一定要和模板中html中input中name属性一致才能验证
        widget=widgets.TextInput(attrs={'id':'a1','class':'c1'}), #设置生成hmtl的样式
        min_length=6,
        error_messages={'required':'用户名不能为空','min_length':'用户名至少6位'}
    )
    pwd=fields.CharField(
        widget=widgets.PasswordInput(),
        max_length=24,
        error_messages={'required':'密码不能为空','max_length':'密码长度不能超过24'}
    )
    email=fields.EmailField(
        error_messages={'required':'用户名不能为空','invalid':'邮箱格式不正确'}
    )    #校验时候出现的错误提示信息

2.views函数处理

from .cmdbforms import myform  #导入定义的form类
def fm(request):
    if request.method=='GET':
        obj=myform()    #生成对象
        return render(request,'form.html',{'obj':obj})#将对象传入到模板中用于生成html
    elif request.method=='POST':
        obj=myform(request.POST)
        if obj.is_valid():    #该方法验证post提交表单的数据是否合法,合法返回True或者否则Flase
            print(obj.clean())  #获取post表单中提交的数据,格式为字典,可以直接用于model操作数据库
            print(obj.cleaned_data)#获取post表单中提交的数据,格式为字典,可以直接用于model操作数据库
            return redirect('/cmdb/fm/')
        else:
            print(obj.errors)
            print(obj.errors.as_json())#json格式的错误
            return render(request,'form.html',{'obj':obj})#当出现错误的时候返回的错误信息

3.HTML模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/cmdb/fm/" method="POST" enctype="multipart/form-data">
    {% csrf_token  %}
    <p>{{ obj.user }} {{ obj.user.errors.0 }}</p>
{# 0代表第一个错误,在这里就是错误的字符串  #}
    <p>{{ obj.pwd }} {{ obj.pwd.errors.0 }}</p>
    <p>{{ obj.email }} {{ obj.email.errors.0 }}</p>
    <input type="submit" value="提交"/>
</form>
</body>
</html>

 

三、form类中的字段和插件介绍

 创建Form类时,主要涉及到字段(fields)和插件(widgets),字段用于对用户请求数据的验证,插件用于自动生成HTML。

1、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,              是否支持本地化,示例场景:utc时间和本地时间转化
    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     是否允许空文件,文件也在clean_data里
 
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类型
    ...
注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串

2、Django内置插件

TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget

3、常用插件示例

# 单radio,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单radio,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.RadioSelect
# )
 
# 单select,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单select,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.Select
# )
 
# 多选select,值为列表
# user = fields.MultipleChoiceField(
#     choices=((1,'上海'),(2,'北京'),),
#     initial=[1,],
#     widget=widgets.SelectMultiple
# )
 
 
# 单checkbox
# user = fields.CharField(
#     widget=widgets.CheckboxInput()
# )
 
 
# 多选checkbox,值为列表
# user = fields.MultipleChoiceField(
#     initial=[2, ],
#     choices=((1, '上海'), (2, '北京'),),
#     widget=widgets.CheckboxSelectMultiple
# )

 

四、初始化数据

 很多时候,我们在一个页面上做增删改查的时候,需要从数据库中将数据拿出来并在页面渲染,那么这些默认值可以使用form直接生成在页面上

方法一:定义form字段中使用initial参数初始化默认选项,但是该默认值不能动态配置(当数据库中选项变化时候,该默认值不会改变)

1.form类定义

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from django.forms import fields
from django.forms import widgets
from django.forms import Form
from django.core.validators import RegexValidator
class appfm(Form):
    user=fields.CharField()
    city=fields.ChoiceField(
        choices=((1, '上海'), (2, '北京'),(3, '上海')),
        widget=widgets.Select
    )
    gender=fields.ChoiceField(
        initial=[2,],               #多选的默认值是一个列表,单选是一个数字
        choices=((1, ''), (2, ''),),
        widget=widgets.CheckboxSelectMultiple(),  #多选插件

    )

2、views函数处理

from .myform import appfm
def test(request):
    user_dict={
        'user':'wd',
        'city':2,
        'gender':1,

    }
    if request.method=='GET':
        obj=appfm(user_dict)#将用户信息传入form对象,最后直接用于获取页面的默认值
        return render(request,'test.html',{'obj':obj})

3、html模版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{ obj.user }}{{ obj.user.errors.0 }}
{{ obj.city }}{{ obj.city.errors.0 }}
{{ obj.gender }}{{ obj.gender.errors.0 }}
</body>
</html>

 方法二:重写form构造方法,在实例化时候设置默认值(该方法是动态的),form定义如下:

class appfm(Form):
    user=fields.CharField()
    city=fields.ChoiceField(
        initial=[1,],
        choices=((1, '上海'), (2, '北京'),(3, '上海')),
        widget=widgets.SelectMultiple
    )
    def __init__(self,*args,**kwargs):
        super(appfm,self).__init__(*args,**kwargs)
        self.initial["city"]=[1,2]  #多选为列表,单选为数字

 

五、form操作动态select标签

场景:当我们在数据库中增加一条数据时候,使用form生成的html标签中的select表现需要是最新数据,所以我们需要在渲染模板之前再次从数据库中获取最新数据。

方式一:form实例化之后,通过修改生成的form对象中封装的choices字段重新赋值,确保是当前最新数据,示例:

form定义

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from cmdb import models
class myform(Form):
    user=fields.CharField(   #字段名字一定要和模板中html中input中name属性一致才能验证
        widget=widgets.TextInput(attrs={'id':'a1','class':'c1'}),
        min_length=6,
        error_messages={'required':'用户名不能为空','min_length':'用户名至少6位'}
    )
    pwd=fields.CharField(
        widget=widgets.PasswordInput(),
        max_length=24,
        error_messages={'required':'密码不能为空','max_length':'密码长度不能超过24'}
    )
    email=fields.EmailField(
        error_messages={'required':'用户名不能为空','invalid':'邮箱格式不正确'}
    ) #校验时候出现的错误提示信息
    user_type=fields.ChoiceField(
        choices=models.Usertype.objects.values_list('id','name'),  #初始化数据
        widget=widgets.Select,
    )
cmdbforms.py

views函数

from .cmdbforms import myform  #导入定义的form类
def fm(request):
    if request.method=='GET':
        obj=myform()    #生成form对象
        obj.fields['user_type'].choices=models.Usertype.objects.values_list('id','name')#form对象中的fields字段封装了我们所定义的验证字段,此时我们从数据库拿到最新的数据,赋值给select标签字段就达到了目的了
        return render(request,'form.html',{'obj':obj})#将对象传入到模板中用于生成html
views.py

html模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/cmdb/fm/" method="POST" enctype="multipart/form-data">
    {% csrf_token  %}
    <p>{{ obj.user }} {{ obj.user.errors.0 }}</p>
{#    0代表第一个错误,在这里就是错误的字符串#}
    <p>{{ obj.pwd }} {{ obj.pwd.errors.0 }}</p>
    <p>{{ obj.email }} {{ obj.email.errors.0 }}</p>
    <p>{{ obj.user_type }}</p>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
form.html

 方式二:实例化之前,通过重新构造方法,使得每一次的实例化都获得最新的数据

form定义

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from cmdb import models
class myform(Form):
    user=fields.CharField(   #字段名字一定要和模板中html中input中name属性一致才能验证
        widget=widgets.TextInput(attrs={'id':'a1','class':'c1'}),
        min_length=6,
        error_messages={'required':'用户名不能为空','min_length':'用户名至少6位'}
    )
    pwd=fields.CharField(
        widget=widgets.PasswordInput(),
        max_length=24,
        error_messages={'required':'密码不能为空','max_length':'密码长度不能超过24'}
    )
    email=fields.EmailField(
        error_messages={'required':'用户名不能为空','invalid':'邮箱格式不正确'}
    ) #校验时候出现的错误提示信息
    user_type=fields.ChoiceField(  #第一种定义方式
        choices=[],
        widget=widgets.Select,
    )
    user_type1=fields.CharField(widget=widgets.Select(choices=[])) #第二种方式定义
    
    def __init__(self,*args,**kwargs):
        super(myform,self).__init__(*args,**kwargs)
        self.fields['user_type'].choices = models.Usertype.objects.values_list('id', 'name')#按照第一种方式定义获取
        self.fields['user_type1'].widget.choices=models.Usertype.objects.values_list('id', 'name')#按照第二种方式定义获取
cmdbforms.py

views函数

def fm(request):
    if request.method=='GET':
        obj=myform()    #生成form对象
        return render(request,'form.html',{'obj':obj})#将对象传入到模板中用于生成html
views.py

html模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/cmdb/fm/" method="POST" enctype="multipart/form-data">
    {% csrf_token  %}
    <p>{{ obj.user }} {{ obj.user.errors.0 }}</p>
{#    0代表第一个错误,在这里就是错误的字符串#}
    <p>{{ obj.pwd }} {{ obj.pwd.errors.0 }}</p>
    <p>{{ obj.email }} {{ obj.email.errors.0 }}</p>
    <p>{{ obj.user_type }}</p>
    <p>{{ obj.user_type1 }}</p>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
form.html

方式三:通过ModelChoiceField、ModelMultipleChoiceField(多选)字段进行自动获取最新数据,但是需要在model定义时候,自定义str方法,用于显示select标签选项。

model定义

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.forms import ModelChoiceField
from cmdb import models
class myform(Form):
    user=fields.CharField(   #字段名字一定要和模板中html中input中name属性一致才能验证
        widget=widgets.TextInput(attrs={'id':'a1','class':'c1'}),
        min_length=6,
        error_messages={'required':'用户名不能为空','min_length':'用户名至少6位'}
    )
    pwd=fields.CharField(
        widget=widgets.PasswordInput(),
        max_length=24,
        error_messages={'required':'密码不能为空','max_length':'密码长度不能超过24'}
    )
    email=fields.EmailField(
        error_messages={'required':'用户名不能为空','invalid':'邮箱格式不正确'}
    ) #校验时候出现的错误提示信息
   
    user_type2=ModelChoiceField(
        queryset=models.Usertype.objects.all(),
        empty_label="请选择",   #多出一项提示语,默认是"---------"
        to_field_name='id',#生成的select标签中value的值
    )
cmdbforms.py

form定义

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.forms import ModelChoiceField
from cmdb import models
class myform(Form):
    user=fields.CharField(   #字段名字一定要和模板中html中input中name属性一致才能验证
        widget=widgets.TextInput(attrs={'id':'a1','class':'c1'}),
        min_length=6,
        error_messages={'required':'用户名不能为空','min_length':'用户名至少6位'}
    )
    pwd=fields.CharField(
        widget=widgets.PasswordInput(),
        max_length=24,
        error_messages={'required':'密码不能为空','max_length':'密码长度不能超过24'}
    )
    email=fields.EmailField(
        error_messages={'required':'用户名不能为空','invalid':'邮箱格式不正确'}
    ) #校验时候出现的错误提示信息
   
    user_type2=ModelChoiceField(
        queryset=models.Usertype.objects.all(),
        empty_label="请选择",   #多出一项提示语,默认是"---------"
    )
cmdbforms.py

view函数

def fm(request):
    if request.method=='GET':
        obj=myform()    #生成form对象
        return render(request,'form.html',{'obj':obj})#将对象传入到模板中用于生成html
views.py

html模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/cmdb/fm/" method="POST" enctype="multipart/form-data">
    {% csrf_token  %}
    <p>{{ obj.user }} {{ obj.user.errors.0 }}</p>
{#    0代表第一个错误,在这里就是错误的字符串#}
    <p>{{ obj.pwd }} {{ obj.pwd.errors.0 }}</p>
    <p>{{ obj.email }} {{ obj.email.errors.0 }}</p>
    <p>{{ obj.user_type }}</p>
    <p>{{ obj.user_type1 }}</p>
    <p>{{ obj.user_type2 }}</p>
    <input type="submit" value="提交"/>
</form>
</body>
</html>
form.html
六、form内置钩子

在form验证的方法中,form验证提供了is_vaild方法进行验证,在这个方法中其实提供了许多的钩子让我们可以自定义验证。

Django1.11版本源码中验证is_vaild内部验证流程为:

obj.is_vaild-->self.errors-->self.full_clean-->self._clean_fields-->self._clean_form-->slef._post_clean,其中我们可以自己根据内置钩子进行验证。

1、self_clean_fields():字段正则表达式验证,里面会执行“clean_字段”方法,我们可以通过自定义该方法对字段进行单独验证。

示例:

form定义

class RegisterForm(Form):
    username=fields.CharField()
    email=fields.EmailField()

    def clean_username(self):  #验证username字段,名字必须为clean_username
        c=models.user.objects.filter(name=self.cleaned_data['username']).count()
        if not c:
            return self.cleaned_data['username']  #通过,将该字段数据返回
        else:
            raise ValidationError('用户名已经存在',code='232')#不通过则抛出异常
    def clean_email(self):
        return self.cleaned_data['email']
my form.py

 views函数

def register(request):
    if request.method=="GET":
        obj=myform.RegisterForm()
        return render(request,'register.html',{'obj':obj})
    if request.method=="POST":
        obj1=myform.RegisterForm(request.POST)
        if obj1.is_valid():#执行内部验证方法
            print(obj1.cleaned_data)
            return redirect('/app01/register/')
        else:
            print(obj1.errors.as_json())
            return render(request, 'register.html', {'obj': obj1})  # 当出现错误的时候返回的错误信息
views.py

2、self._clean_form:对整体字段进行验证,执行该方法时候会执行self.clean()方法,所以通过定义self.clean()方法,可以对字段进行整体验证。

 tips:当使用_clean_form进行整体验证的时候错误信息放在了errors中的__all__或者NON_FIELD_ERRORS(from django.core.exceptions import NON_FIELD_ERRORS)字段里面。

class LoginForm(Form):
    username=fields.CharField()
    pwd=fields.CharField()
    def clean(self):
        c=models.user.objects.filter(name=self.cleaned_data['username'],
                                     pwd=self.cleaned_data['pwd']).count()
        if c :
            return self.cleaned_data  #将正确结果返回
        else:
            raise ValidationError('用户名或者密码错误')
示例

3、slef._post_clean:还可以再一次进行验证,定义方法同self.clean()

class LoginForm(Form):
    username=fields.CharField()
    pwd=fields.CharField()

    def _post_clean(self):
        pass
示例

 

六、自定义验证规则

方式一:

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
    user = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
    )

方式二:

import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
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('手机号码格式错误')
 
 
class PublishForm(Form):
 
 
    title = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': '标题不能为空',
                                            'min_length': '标题最少为5个字符',
                                            'max_length': '标题最多为20个字符'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': '标题5-20个字符'}))
 
 
    # 使用自定义验证规则
    phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={'required': '手机不能为空'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手机号码'}))
 
    email = fields.EmailField(required=False,
                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

方式三:自定义方法

from django import forms
    from django.forms import fields
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    from django.core.validators import RegexValidator
 
    class FInfo(forms.Form):
        username = fields.CharField(max_length=5,
                                    validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )
        email = fields.EmailField()
 
        def clean_username(self):
            """
            Form中字段中定义的格式匹配完之后,执行此方法进行验证
            :return:
            """
            value = self.cleaned_data['username']
            if "666" in value:
                raise ValidationError('666已经被玩烂了...', 'invalid')
            return value

方式四:同时生成多个标签进行验证

from django.forms import Form
from django.forms import widgets
from django.forms import fields
 
from django.core.validators import RegexValidator
 
 
############## 自定义字段 ##############
class PhoneField(fields.MultiValueField):
    def __init__(self, *args, **kwargs):
        # Define one message for all fields.
        error_messages = {
            'incomplete': 'Enter a country calling code and a phone number.',
        }
        # Or define a different message for each field.
        f = (
            fields.CharField(
                error_messages={'incomplete': 'Enter a country calling code.'},
                validators=[
                    RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),
                ],
            ),
            fields.CharField(
                error_messages={'incomplete': 'Enter a phone number.'},
                validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],
            ),
            fields.CharField(
                validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],
                required=False,
            ),
        )
        super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,
                                         **kwargs)
 
    def compress(self, data_list):
        """
        当用户验证都通过后,该值返回给用户
        :param data_list:
        :return:
        """
        return data_list
 
############## 自定义插件 ##############
class SplitPhoneWidget(widgets.MultiWidget):
    def __init__(self):
        ws = (
            widgets.TextInput(),
            widgets.TextInput(),
            widgets.TextInput(),
        )
        super(SplitPhoneWidget, self).__init__(ws)
 
    def decompress(self, value):
        """
        处理初始值,当初始值initial不是列表时,调用该方法
        :param value:
        :return:
        """
        if value:
            return value.split(',')
        return [None, None, None]
七、form内置序列化错误信息

1、使用django自带的序列化serializers函数,这个方法缺点是函数内部序列化为什么内容就返回什么内容,不能自定制。

from django.core import serializers
ret = models.BookType.objects.all()
data = serializers.serialize("json", ret)

2、使用json自定义endocoder进行序列化操作

原理是:通过cls参数制定处理的类,每一次序列化操作都会调用该类中的default方法。

有些时候我们使用json.dumps方法序列化操作时候,并不能完全序列化,会报错,比如,在数据datetime日期时候,此时我们可以通过自定义序列化类来处理

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
import json
from datetime import date
from datetime import datetime
a={'k1':"v1",'date':datetime.now()}
class JsonCustomEncoder(json.JSONEncoder):   #自定义处理类
    def default(self, field):
        if isinstance(field, datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field, date):
            return field.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, field)
b=json.dumps(a,cls=JsonCustomEncoder)
print(b)

应用场景:ajax提交数据时候,Django进行数据验证返回给的错误数据时候使用,示例:

def fm(request):
    if request.method=='GET':
        obj=myform()    #生成form对象
        return render(request,'form.html',{'obj':obj})#将对象传入到模板中用于生成html
    elif request.method=='POST':
        obj=myform(request.POST)
        if obj.is_valid():    #该方法验证post提交表单的数据是否合法,合法返回True或者否则Flase
            print(obj.clean())  #获取post表单中提交的数据,格式为字典,可以直接用于model操作数据库
            print(obj.cleaned_data)#获取post表单中提交的数据,格式为字典,可以直接用于model操作数据库

            return redirect('/cmdb/fm/')
        else:
    #######自定义序列化类进行错误序列化###
            from django.core.exceptions import ValidationError
            import json
            class JsonCustomEncoder(json.JSONEncoder):
                def default(self, field):
                    if isinstance(field, ValidationError):
                        return {'code':field.code,'message':field.messages}
                    else:
                        return json.JSONEncoder.default(self, field)
            ret={'status':None,'message':None,'code':None}
            ret['error']=obj.errors.as_data()
            res=json.dumps(ret,cls=JsonCustomEncoder)
            return HttpResponse(res)    

 

posted @ 2017-10-17 23:56  W-D  阅读(1256)  评论(0编辑  收藏  举报