一、表单常用字段类型及参数
表单可以自动生成html代码,每一个字段默认有一个html显示样式,大多数默认为输入框。
字段相当于正则表达式的集合,能够对表单传入的数据进行校验,并且某一部分校验失败时会保留另一部分校验成功的值。
使用表单时首先需要导入forms模块和forms的fields模块,再定义一个继承forms.Form的类,实例化后在模板中通过调用实例化对象的属性生成输入框。
field:这个一般不直接使用,用于被其他字段继承
required=True, 是否允许为空
label=None, 用于生成显示内容,如果不定义label,则显示的内容为首字母大写的字段名
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 例如 {'required': '不能为空', 'invalid': '格式错误'},是一个字典,key表示每一个表达式的code,value表示如果在对应的code上发生错误,后台可以捕捉到的错误信息并返给前台与用户交互,其中value又是一个列表
show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[], 自定义验证规则
localize=False, 是否支持本地化
disabled=False, 是否可以编辑
label_suffix=None Label内容后缀
widget=None, 生成HTML插件
1.BooleanField(Field):布尔值,相当于单选框
2.CharField(Field):字符串
max_length最大长度,min_length最小长度,strip是否移除用户输入的空格
3.EmailField(CharField):邮箱
invalid格式是否合法
4.IntegerField(Field):整数
invalid格式是否合法、max_value最大值、min_value最小值
5.FloatField(IntegerField):浮点类型
6.DecimalField(IntegerField):浮点类型
max_digits(总长度)、decimal_places(小数位长度)
7.FileField(Field):上传文件、ImageField(Field)上传图片
allow_empty_file是否允许空文件,上传文件需要在form表单指定enctype="multipart/form-data",并且要通过request.FILES接收
upload_to:指定上传路径
8.ChoiceField(Field):单选下拉列表
9.MultipleChoiceField(ChoiceField):多选下拉列表
10.TypedChoiceField(ChoiceField))
可对传入的数据进行一次类型转化coerce = lambda x: int(x)
11.GenericIPAddressField(CharField):ip地址
protocol='both'表示同时支持ipv4和ipv6的地址
12.DateField(BaseTemporalField),日期,2019-05-10,
13.TimeField(BaseTemporalField),时间,14:23
14.DateTimeField(BaseTemporalField),日期和时间,2019-05-10 14:23
15.URLField:接收URL格式的字符串
二、django内置插件
使用django的内置html插件时,需要先导入forms模块的widgets模块,from django.forms import widgets。
对于CharField字段,默认显示的是文本框,如果希望显示成单选、多选或下拉列表的形式,则需要用到widgets定制插件。
TextInput(Input)
EmailInput(TextInput)
NumberInput(TextInput)
RadioSelect(ChoiceWidget)
Select(ChoiceWidget)
SelectMultiple(Select)
CheckboxInput(Input)
CheckboxSelectMultiple(ChoiceWidget)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
URLInput(TextInput)
PasswordInput(TextInput)
FileInput
HiddenInput(TextInput)
NullBooleanSelect
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
1.定制单选radio、单选下拉列表、多选下拉列表、单选框和多选框:通过CharField、ChoiceField和MutipleChoiceField字段
对于单选radio,第一种写法使用CharField字段,该字段默认显示为输入框,通过widget=widgets.RadioSelect插件定制为单选框样式,并将choices作为参数传递给该插件。
第二种写法使用ChoiceField字段,该字段默认显示为下拉列表,通过widget=widgets.RadioSelect插件定制为单选框样式,并将choices作为参数传递给该字段。
通过widgets定制插件时,可同时给字段加上自定义属性,例如如下hobby8和hobby9。
#单选radio hobby1 = fields.CharField(label='爱好1',widget=widgets.RadioSelect(choices=((1,'篮球'),(2,'足球'),(3,'乒乓球'))),initial=1) hobby2 = fields.ChoiceField(label='爱好2',widget=widgets.RadioSelect,choices=((1,'篮球'),(2,'足球'),(3,'乒乓球')),initial=1) #单选下拉列表 hobby3 = fields.CharField(label='爱好3',widget=widgets.Select(choices=((1,'篮球'),(2,'足球'),(3,'乒乓球'))),initial=3) hobby4 = fields.ChoiceField(label='爱好4', widget=widgets.Select,choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')), initial=3) #多选下拉列表 hobby5 = fields.CharField(label='爱好5', widget=widgets.SelectMultiple(choices=((1, '篮球'), (2, '足球'), (3, '乒乓球'))), initial=[1, 2]) hobby6 = fields.MultipleChoiceField(label='爱好6', widget=widgets.SelectMultiple,choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')), initial=[1,2]) #单选框 hobby7 = fields.CharField(label='是否同意',widget=widgets.CheckboxInput()) #多选框 hobby8 = fields.CharField(label='爱好8', widget=widgets.CheckboxSelectMultiple(choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')),attrs={'name':'hobby8'}), initial=[1, 2]) hobby9 = fields.MultipleChoiceField(label='爱好9',widget=widgets.CheckboxSelectMultiple(attrs={'name':'hobby9'}),choices=((1, '篮球'), (2, '足球'), (3,'乒乓球'),initial=[1, 2])
2.扩展正则
如果某一字段自带的验证方法不足以进行验证,可以自定义验证方法,这需要通过RegexField字段或者validators=[]参数完成
#单选radio hobby1 = fields.CharField(label='爱好1',widget=widgets.RadioSelect(choices=((1,'篮球'),(2,'足球'),(3,'乒乓球'))),initial=1) hobby2 = fields.ChoiceField(label='爱好2',widget=widgets.RadioSelect,choices=((1,'篮球'),(2,'足球'),(3,'乒乓球')),initial=1) #单选下拉列表 hobby3 = fields.CharField(label='爱好3',widget=widgets.Select(choices=((1,'篮球'),(2,'足球'),(3,'乒乓球'))),initial=3) hobby4 = fields.ChoiceField(label='爱好4', widget=widgets.Select,choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')), initial=3) #多选下拉列表 hobby5 = fields.CharField(label='爱好5', widget=widgets.SelectMultiple(choices=((1, '篮球'), (2, '足球'), (3, '乒乓球'))), initial=[1, 2]) hobby6 = fields.MultipleChoiceField(label='爱好6', widget=widgets.SelectMultiple,choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')), initial=[1,2]) #单选框 hobby7 = fields.CharField(label='是否同意',widget=widgets.CheckboxInput()) #多选框 hobby8 = fields.CharField(label='爱好8', widget=widgets.CheckboxSelectMultiple(choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')), attrs={'name':'hobby7'}), initial=[1, 2]) hobby9 = fields.MultipleChoiceField(label='爱好9',widget=widgets.CheckboxSelectMultiple(attrs={'name':'hobby8'}), choices=((1, '篮球'), (2, '足球'), (3,'乒乓球'),initial=[1, 2])
通过validators=[]属性定制时,可以自定义多个规则RegexValidator,每一个规则的第一个参数为规则,第二个规则为对应的错误信息
通过RegexField字段定制时,只能定制一个规则。
三、表单使用实例
1.forms的py文件
class UserForm(forms.Form): username = fields.CharField(label='姓名',required=True,max_length=15,error_messages={'max_length':'too long'}) age = fields.IntegerField(label='年龄',required=True,min_value=18,error_messages={'invalid':'请输入整数','min_value':'不符合劳动法'}) birth = fields.DateField(label='入职时间',required=True) email = fields.EmailField(label='邮箱',required=True,error_messages={'invalid':'请输入有效格式的邮箱'}) gender = fields.CharField(label='性别', widget=widgets.RadioSelect(choices=((1, '男'), (2, '女'))), initial=1) resume = fields.FileField(label='简历') city = fields.CharField(label='省份',widget=widgets.Select()) hobby = fields.MultipleChoiceField(label='爱好',widget=widgets.CheckboxSelectMultiple(attrs={'name':'hobby8'}), choices=((1, '篮球'), (2, '足球'), (3, '乒乓球')),initial=[1,2]) proto = fields.CharField(label='是否已阅协议', widget=widgets.CheckboxInput()) def __init__(self,*args,**kwargs): super(UserForm,self).__init__(*args,**kwargs) self.fields['city'].widget.choices = Province.objects.all().values_list() #假设定义了一个省份的类存放在数据库,为了数据库的内容变化能在页面刷新时显示到页面上,需要进行init操作并将数据库的值赋值给city字段
2.处理函数的py文件
def add_user(request): if request.method == 'GET': obj = UserForm() #实例化一个空的表单 return render(request,'add_user.html',{'obj':obj}) elif request.method =='POST': obj = UserForm(request.POST,request.FILES) #传入的文件需要通过request.FILES接收,其他通过request.POST接收,一起传递给表单类进行实例化 if obj.is_valid(): #判断输入是否有效 print(type(obj.fields)) print(obj.fields) print(type(obj.cleaned_data)) print(obj.cleaned_data) #打印输入内容 print(obj.cleaned_data['username']) #获取输入的username值 print(obj.cleaned_data.get('email')) #获取输入的email值 UserInfo.object.create(**obj.cleaned.data) # UserForm表单对应了数据库中的表UserInfo else: print(type(obj.errors)) print(obj.errors.get_json_data())#打印错误信息,如果需要打印错误信息,还需要在form中指定novalidate属性 return render(request,'add_user.html',{'obj':obj}) else: return redirect('http://www.baidu.com')
①obj.is_valid()方法
obj.is_valid()表示判断输入是否满足Form各字段的要求,实例化一个非空的表单,在实例化的时候并没有生成html标签,也没有进行表单验证,生成html标签并且进行表单验证是在is_valid()时进行的。obj.cleaned_data表示键值和对应的输入值。
如果在实例化表单的时候通过inital传值,即obj = UserForm(inital = (request.POST,request.FILES) ),在实例化的时候不会进行表单验证。
②obj.fields属性
obj.fields表示表单的所有字段,类型为<class 'collections.OrderedDict'>。
③obj.cleaned_data属性
在is_valid之前没有cleaned_data,cleaned_data是在进行验证之后才生成,类型为<class 'dict'>
④obj.errors
obj.errors表示错误信息,类型为<class 'django.forms.utils.ErrorDict'>
错误信息默认是以as_ul()进行显示的,其他显示方式还有as_data()、as_json()、as_text()、get_json_Data(),常用get_json_Data()。
3.模板文件
在渲染form对象时,可以以as_table表格、as_ul无序列表、as_p段落等方式进行渲染。
<form action="add_user" method="post" enctype="multipart/form-data"> <!--如果需要上传文件,则form中应指定enctype属性为multipart/form-data--> {% csrf_token %} <table> {{ obj.as_table }} <!--将表单以表格形式渲染--> </table> <input type="submit"> </form>
页面显示内容为,并且如果按照要求输入数据,pycharm打印的信息如下。
如果在form中指定invalidate属性,假设不输入年龄、不按格式输入邮箱,那么打印的错误信息如下
另外一种模板渲染方式
<form action="add_user" method="post" enctype="multipart/form-data" novalidate> <!--如果需要上传文件,则form中应指定enctype属性为multipart/form-data--> {% csrf_token %} <p>{{ obj.username.label }}{{ obj.username }}{{ obj.errors.0 }}</p> <p>{{ obj.age.label }}{{ obj.age }}{{ obj.errors.0 }}</p> <p>{{ obj.birth.label }}{{ obj.birth }}{{ obj.errors.0 }}</p> <p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.0 }}</p> <p>{{ obj.gender.label }}{{ obj.gender }}{{ obj.errors.0 }}</p> <p>{{ obj.resume.label }}{{ obj.resume }}{{ obj.errors.0 }}</p> <p>{{ obj.city.label }}{{ obj.city }}{{ obj.errors.0 }}</p> <p>{{ obj.hobby.label }}{{ obj.hobby }}{{ obj.errors.0 }}</p> <p>{{ obj.proto.label }}{{ obj.proto }}{{ obj.errors.0 }}</p> <input type="submit"> </form>
四、表单与数据库是否重复验证
上述方法已经可以可以对各字段进行格式、大小、长度等验证,但是表单提交的数据通常要写入数据库,为了防止写入违反唯一性约束的两条相同记录,还需要验证增加的数据是否已经在数据库中存在。
1.对于单一字段
可在表单类时同时定义表单的方法,方法名称为clean_验证字段名,假如上述例子UserForm表单对应了的数据库中的表UserInfo,假如UserInfo表中的username不可重复,那么在定义表单时可通过增加clean_username()方法实现。
#from django.core.exceptions import ValidationError导入异常类 class UserForm(forms.Form): username = fields.CharField(label='姓名',required=True,max_length=15,error_messages={'max_length':'too long'}) …… def clean_username(self): #定义clean_字段名的方法 v= self.cleaned_data['username'] # if UserInfo.objects.filter(username=v).count(): # raise ValidationError('用户名已经存在','repeat') #抛出异常,第一个参数为error_message,第二个为code return v
2.对于多个字段
在定义表单类的同时定义clean()方法,假如username和age字段不可同时重复
#from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class UserForm(forms.Form): …… def clean(self): value_dic = self.cleaned_data v1 = value_dic.get('username') v2 = value_dic.get('email') if UserInfo.objects.filter(username=v1,email=v2).count(): raise ValidationError('total error') return value_dic
因为这一类错误不属于单一的字段,因此在errors中显示的错误字段为__all__(也即NON_FIELD_ERRORS),而在模板页面只能通过v.non_field_errors(小写)获取整体错误信息,不能通过v.__all__获取。
表单字段类型可参考http://www.cnblogs.com/wupeiqi/articles/6144178.html