一. Django的Form组件的基本使用
1. Form组件的主要功能
<1>生成HTML标签
<2>验证用户数据(显示错误信息)
<3>HTML Form提交保留上次提交数据
<4>初始化页面显示内容
2. Form组件基本流程
创建Form类->views函数处理,验证用户输入->生成HTML并保留提交内容
2.1 Form表单提交方式
<1>创建Form类
#-*-coding=utf-8-*- from django.core.exceptions import ValidationError from django.forms import Form from django.forms import widgets from django.forms import fields class MyForm(Form): user = fields.CharField( required=True, min_length=3, max_length=12, label='用户名', error_messages={'required':'用户名不能为空','min_length':'用户名长度为3-12之间','max_length':'用户名长度为3-12之间'}, #自定制HTML标签和属性 widget=widgets.TextInput(attrs={'id': 'user_name', 'class': 'name'}) ) age = fields.IntegerField( label='年龄', required=True, error_messages={'required':'年龄不能为空','invalid':'非有效输入'} ) gender = fields.ChoiceField( label='性别', choices=((1, '男'), (2, '女'),), initial=2,#默认初始值 widget=widgets.RadioSelect ) city = fields.CharField( label='城市', initial=2, widget=widgets.Select(choices=((1, '上海'), (2, '北京'),)) ) pwd = fields.CharField( required=True, min_length=6, max_length=12, label='密码', error_messages={'required': '密码不能为空', 'min_length': '密码长度为6-12', 'max_length': '用户名长度为6-12'}, #widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) )
<2>views函数处理,验证用户输入
在views函数里面,以GET方式请求时,创建Form对象,返回页面,并将对象渲染到页面,然后用户提交数据,以POST方式发起请求,在POST请求时,将post方式提交的数据传给Form对象,然后验证数据的合法性,如果合法,页面将保留提交的合法数据,如果数据不合法,将错误信息返回到当前页面并渲染出来
#-*-coding=utf-8-*- from django.shortcuts import render from student_manage.views.form1 import * def index1(request): if request.method == 'GET': #生成空的html标签,含有默认值 obj= MyForm() return render(request,'MyForm.html',{'obj':obj}) else: obj=MyForm(request.POST,request.FILES)#拿到浏览器请求,传入form做验证 if obj.is_valid():#判断验证是否通过 print('验证成功', obj.cleaned_data)#验证通过后的值是字典形式 else: print('验证失败', obj.errors)#返回的错误信息是个对象,'obj.errors.字典.0 ' 获取第一个错误信息 return render(request,'MyForm.html',{'obj':obj})
<3>生成HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/MyForm.html" method="POST" enctype="multipart/form-data"> {% csrf_token %} <p>{{obj.user.label }}{{ obj.user }} {{ obj.user.errors }}</p> <p>{{obj.age.label }}{{ obj.age }} {{ obj.age.errors}}</p> <p>{{obj.gender.label }}{{ obj.gender }} {{ obj.gender.errors }}</p> <p>{{obj.city.label }}{{ obj.city }} {{ obj.city.errors }}</p> <p>{{obj.pwd.label }}{{ obj.pwd }} {{ obj.pwd.errors }}</p> <input type="submit" value="提交"/> </form> </body> </html>
2.2 Ajax提交方式
创建Form类->views函数处理,验证用户输入->返回HttpResponse,与ajax交互->前端:验证成功,跳转到新页面,验证失败,在当前页面添加错误信息
<1>创建Form类
与上面相同
<2>views函数处理,验证用户输入
在views函数里面,以GET方式请求时,创建Form对象,返回页面,并将对象渲染到页面,然后用户提交数据,以POST方式发起请求,在POST请求时,将post方式提交的数据传给Form对象,然后验证数据的合法性,如果合法,通过HttpResponse将相应的状态信息传递给ajax进行判断,判断成功后跳转到新页面,如果数据不合法,通过HttpResponse将相应的状态和错误信息返回到ajax的arg中,在当前页面自己添加错误信息
from django.shortcuts import render,HttpResponse from student_manage.views.form1 import * from django.forms.utils import ErrorDict import json def ajax(request): if request.method == 'GET': #生成空的html标签,含有默认值 obj= MyForm() return render(request, 'ajax.html', {'obj': obj}) else: response={'status':'Verification failed','message':None} obj = MyForm(request.POST, request.FILES) # 拿到浏览器请求,传入form做验证 if obj.is_valid(): # 判断验证是否通过 print('验证成功', obj.cleaned_data) response['status']= 'Verification pass' print('response:',response) return HttpResponse(json.dumps(response)) else: # print(obj.errors.as_ul()) #print(obj.errors.as_json()) print('验证失败',obj.errors.as_data()) response['message'] = obj.errors return HttpResponse(json.dumps(response))
<3>生成HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form id=fm action="/ajax.html" method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ obj.as_p}} <input type="button" value="提交" id="saveBtn"/> </form> <script src="/static/jquery-3.1.1.js"></script> <script> $(function () { bind_save() }); function bind_save() { $("#saveBtn").click(function () { $.ajax({ url: '/ajax.html', type: 'POST', data: $('#fm').serialize(), dataType: 'JSON', success: function (arg) { if (arg.status == 'Verification pass') { console.log(arg); window.location.href = "http://www.baidu.com"; } else { console.log(arg.message); $.each(arg.message,function (key,value) { console.log(key,value); var tag=document.createElement("span"); tag.innerHTML=value[0]; $('#fm').find('input[name="'+key+'"]').after(tag); }); } } }) }) } </script> </body> </html>
注意:在生成HTML中的错误信息时,用到了jquery中$each()方法处理json数据
二. Form组件的主要字段和插件
1. Form组件的主要字段
Form字段用于对用户请求数据的验证,本质就是封装的正则表达式,含有默认HTML插件
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, 是否支持本地化 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 是否允许空文件 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类型
2. Form组件的主要插件
插件用于自动生成HTML ,本质是调用__str__方法
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 # )
4. 动态绑定数据库
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ,获取的值无法实时更新,故需要自定义构造方法,当数据库数据改变时,能随之实时更新
实质是form组件中每个字段都是类的数据属性(全局变量),在类每次实例化之后,数据属性不会发生改变,会保留上次的更新结果导致无法动态显示数据库的内容
<动态绑定数据库 法1>
每次实例化之前,更新字段的值
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.ChoiceField( initial=2, widget=widgets.Select ) def __init__(self, *args, **kwargs): super(MyForm,self).__init__(*args, **kwargs) self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
<动态绑定数据库 法2>
使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现,不推荐使用此法,因为其依赖于models中的__str__方法
from django.forms import models as form_model student_classes=form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all()) # models中: # def __str__(self): # return self.title
三. 自定义验证规则和方法
1.自定义验证规则
<1>法1
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开头')], )
<2>法2
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'邮箱'}))
2.自定义验证方法
基于源码流程扩展验证方法
<1>设计数据库
from django.db import models class UserInfo(models.Model): user=models.CharField(max_length=12) age=models.IntegerField() gender=models.IntegerField() city=models.CharField(max_length=32) pwd=models.CharField(max_length=12) email=models.CharField(max_length=32) def __str__(self): return self.user
<2>设计相关form字段和自定义验证方法
#-*-coding=utf-8-*- from django.core.exceptions import ValidationError from django.forms import Form from django.forms import widgets from django.forms import fields from student_manage.models import * from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class MyForm(Form): user = fields.CharField( required=True, min_length=3, max_length=12, label='用户名', error_messages={'required':'用户名不能为空','min_length':'用户名长度为3-12之间','max_length':'用户名长度为3-12之间'}, #自定制HTML标签和属性 widget=widgets.TextInput(attrs={'id': 'user_name', 'placeholder': '用户名'}) ) age = fields.IntegerField( label='年龄', required=True, widget=widgets.TextInput(attrs={'placeholder': '年龄'}), error_messages={'required':'年龄不能为空','invalid':'非有效输入'} ) gender = fields.IntegerField( label='性别', initial=2,#默认初始值 widget=widgets.RadioSelect(choices=((1, '男'), (2, '女'),)) ) city = fields.CharField( label='城市', initial=2, widget=widgets.Select(choices=((1, '上海'), (2, '北京'),)) ) pwd = fields.CharField( required=True, min_length=6, max_length=12, label='密码', widget=widgets.PasswordInput( attrs={"class": "form-control", "placeholder": "密码,必须包含数字,字母!", }), error_messages={'required': '密码不能为空', 'min_length': '密码长度为6-12', 'max_length': '用户名长度为6-12'}, #widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) ) email=fields.EmailField( required=True, max_length=32, widget=widgets.TextInput( attrs={ "class": "form-control", "placeholder": "请输入您常用邮箱", }), error_messages={'required':'邮箱不能为空','invalid':'邮箱格式错误'} ) #自定义方法:clean_字段名 #必须返回值self.cleaned_data['字段名'] #如果出错,raise ValidationError('用户名以存在') def clean_user(self): usernameValue=self.cleaned_data['user'] if UserInfo.objects.filter(user=usernameValue).count(): raise ValidationError('用户名已存在') return usernameValue def clean(self): print(self.cleaned_data) value_dict=self.cleaned_data username=value_dict.get('user') email=value_dict.get('email') if UserInfo.objects.filter(user=username,email=email).count(): raise ValidationError('用户名和邮箱联合已存在') #验证失败 {'__all__': [ValidationError(['用户名和邮箱联合已存在'])]} #整体的错误信息,在前段取值时用键__all__取 return self.cleaned_data
<3>在views视图中进行相关处理
from django.shortcuts import render,HttpResponse from student_manage.views.form1 import * from django.forms.utils import ErrorDict from student_manage.models import * import json def ajax(request): if request.method == 'GET': #生成空的html标签,含有默认值 obj= MyForm() return render(request, 'ajax.html', {'obj': obj}) else: response={'status':'Verification failed','message':None} obj = MyForm(request.POST, request.FILES) # 拿到浏览器请求,传入form做验证 if obj.is_valid(): # 判断验证是否通过 print('验证成功', obj.cleaned_data) response['status']= 'Verification pass' print('response:',response) UserInfo.objects.create(**obj.cleaned_data) return HttpResponse(json.dumps(response)) else: # print(obj.errors.as_ul()) #print(obj.errors.as_json()) print('验证失败',obj.errors.as_data()) response['message'] = obj.errors return HttpResponse(json.dumps(response))
<4>HTML设计
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form id=fm action="/ajax.html" method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ obj.as_p}} <input type="button" value="提交" id="saveBtn"/> </form> <script src="/static/jquery-3.1.1.js"></script> <script> $(function () { bind_save() }); function bind_save() { $("#saveBtn").click(function () { $.ajax({ url: '/ajax.html', type: 'POST', data: $('#fm').serialize(), dataType: 'JSON', success: function (arg) { if (arg.status == 'Verification pass') { console.log(arg); window.location.href = "http://www.baidu.com"; } else { console.log(arg.message); $.each(arg.message,function (key,value) { console.log(key,value); var tag=document.createElement("span"); tag.innerHTML=value[0]; $('#fm').find('input[name="'+key+'"]').after(tag); }); } } }) }) } </script> </body> </html>
3.Form组件错误信息总结
对于Form组件的错误信息: 注意再注意: 默认错误信息方式: raise ValidationError("输入的密码不一致!") 自定义对已拿到所有字段数据进行验证的时候,这种方式在获取错误信息时就发生改变,查看源码发现如果有错误的话,他会执行self.add_error(None, e) 通过方法给字段None添加错误信息,查看add_error的源码以及执行过程,发现None = NON_FIELD_ERRORS。也就是说会默认给__all__字段或 NON_FIELD_ERRORS写上错误信息。原因:内部定义的NON_FIELD_ERRORS="__all__", 获取方式: 前端获取 是通过obj.non_field_errors 后台获取是通过 obj.errors["__all__"] or obj.errors[NON_FIELD_ERRORS] 我们知道,当报错的时候,内部会自动去添加错误信息,那我们能否手动指定某个字段的错误信息呢?答案是肯定的。 这样我们自己添加异常的错误信息,就能直接通过{{obj.errors.passwords.0}}获取到与其他的无疑。 语法: self.add_error('字段名称', 错误异常对象)
四. 取数据库数据->序列化->传给前端ajax进行相应处理
<1>models.py和form1.py
数据库设计和字段验证规则设计与上同
<2>在views视图中进行序列化
from django.shortcuts import render,HttpResponse from student_manage.views.form1 import * from django.forms.utils import ErrorDict from student_manage.models import * from django.core import serializers import json def serialize(request): return render(request, 'serialize.html') def get_data(request): ret={'status':True,'data':None} try: #user_list=UserInfo.objects.all() #user_list结构:QuerySet【obj,obj,obj】 #serializers只能序列化QuerySet对象,并且只有QuerySet里面为一个个对象时,才需要用serializers序列化, #QuerySet里面如果是字典或列表,则直接转化为python基本数据,然后直接用json序列化 #ret['data']=serializers.serialize('json',user_list) #user_list = UserInfo.objects.all().values('id', 'user','age','gender','city','email') #ret['data'] = list(user_list) # 前端直接打印console.log(arg.data); #[{'id': 1, 'user': 'candly', 'age': 12, 'gender': True, 'city': '成都', 'email': 'love@163.com'}, # {'id': 2, 'user': 'lucyck', 'age': 29, 'gender': 2, 'city': '北京', 'email': 'like@163.com'}] user_list = UserInfo.objects.all().values('id', 'user', 'age', 'gender', 'city', 'email') print('user_list:',user_list) ret['data'] = list(user_list) #console.log(arg.data); except Exception as e: ret['status'] = False result = json.dumps(ret) print('ret:',ret) return HttpResponse(result)
<3>前端接受数据进行相应处理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>用户列表</h1> <table id="tb"> <thead > <tr id="th"> </tr> </thead> <tbody > <tr id="tb"> </tr> </tbody> </table> <script src="/static/jquery-3.1.1.js"></script> <script> $(function () { initData(); }); function initData() { $.ajax({ url :'/get_data', type:'GET', dataType:'JSON', success:function (arg) { console.log(arg); if(arg.status) { //$('#tb').append(arg); // var data = JSON.parse(arg.data); console.log(arg.data); $.each(arg.data,function (i,item) { $.each(item,function (key,value) { console.log(key,value); }) }) } } }) } </script> </body> </html>
五. 补充知识点:文件上传
相关代码如下
from django.forms import Form from django.forms import widgets from django.forms import fields from django.shortcuts import render,HttpResponse class UploadForm(Form): user = fields.CharField() img = fields.FileField() def upload_file(request): if request.method == 'GET': return render(request, 'upload.html') if request.method=='POST': #不通过form,直接上传 #user = request.POST.get('user') #img = request.FILES.get('img') # img是对象(文件大小,文件名称,文件内容。。。) #print(img.name) #print(img.size) #f = open(img.name, 'wb') #for line in img.chunks(): #f.write(line) #f.close() #return HttpResponse('...') #通过form上传 obj = UploadForm(request.POST, request.FILES) if obj.is_valid(): user = obj.cleaned_data['user'] img = obj.cleaned_data['img'] print(img.name,img.size) f=open(img.name,'wb') for line in img.chunks(): f.write(line) f.close() return HttpResponse('...')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/upload" method="POST" enctype="multipart/form-data"> {% csrf_token %} <input type="text" name="user" /> <div style="position: relative"> <a>文件上传</a> <input type="file" name="img" style="opacity: 0;position:absolute;top:0;left: 0;" /> </div> <input type="submit" value="提交" /> </form> </body> </html>
>>>>>待续
来源:https://www.cnblogs.com/wuxunyan/p/9208317.html