django 之 Form组件
一、初识:
Django框架的Form组件主要具有一下几大功能:
生成HTML标签
验证用户数据(显示错误信息)
HTML Form提交保留上次提交数据
初始化页面显示的内容
主要用途:
1、利用组件特性,初始化页面form表单中自动生成input标签,省却自定义大量input标签。
2、通过内置的正则匹配规则,可以反复的对用户提交数据进行校验,批量获取form标签提交的数据(以字典形式显示),并在后台进行处理,若有错误则在页面显示错误信息。
3、重要一点,在页面保留用户提交的数据,不会因为页面刷新而消失。
4、可以自定义验证规则,再Form组件内置的方法不能满足需求的时候,我们就可以自定制更高段的验证规则。
写在前面:
1、利用form表单提交,由于表单特性,提交之后页面会刷新,数据没办法再页面保留。
- 代码举例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
- Form提交 - views.py后台函数处理 class XXForm(Form): user = fields.CharField(min_length=8) email = fields.EmailField() password = fields.CharField() phone = fields.RegexField('139\d+') def login(request): if request.method == 'GET': return render(request,'login.html') else: obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) return redirect('http://www.baidu.com') return render(request,'login.html',{'obj': obj}) - login.html 页面 <form id="f1" action="/login/" method="POST"> {% csrf_token %} <p> <input type="text" name="user" />{{ obj.errors.user.0 }} </p> <p> <input type="password" name="pwd" />{{ obj.errors.pwd.0 }} </p> <input type="submit" value="提交" /> </form> ======> 无法保留上次输入内容
2、ajax方式提交,这种方式近乎于完美,在页面不刷新的情况下实现往后台提交数据的功能,也可以把从后台返回的值(或是用户输入的值)放在相对应的标签中。
但是这样做的话,有没有想过代码量太大的问题,同时还要处理错误信息的显示。小型的认证页面还没什么问题,若是一个繁琐的认证页面呢?
- 代码举例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
- Ajax提交 views.py后台函数处理 class XXForm(Form): user = fields.CharField(min_length=8) email = fields.EmailField() password = fields.CharField() phone = fields.RegexField('139\d+') def login(request): return render(request,'login.html') def ajax_login(request): import json ret = {'status': True,'msg': None} obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: # print(obj.errors) # obj.errors对象 ret['status'] = False ret['msg'] = obj.errors v = json.dumps(ret) return HttpResponse(v) - ajax_login.html 页面 <form id="f1" action="/login/" method="POST"> {% csrf_token %} <p> <input type="text" name="user" /> </p> <p> <input type="password" name="pwd" /> </p> <input type="submit" value="提交" /> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function submitForm(){ $('.c1').remove(); $.ajax({ url: '/ajax_login/', type: 'POST', data: $('#f1').serialize(),// user=alex&pwd=456&csrftoen=dfdf\ dataType:"JSON", success:function(arg){ console.log(arg); if(arg.status){ }else{ $.each(arg.msg,function(index,value){ console.log(index,value); var tag = document.createElement('span'); tag.innerHTML = value[0]; tag.className = 'c1'; $('#f1').find('input[name="'+ index +'"]').after(tag); }) } } }) } </script>
二、Form组件使用基本语法、参数、字段:
1、使用Form组件,需要导入三个模块
from django.forms import Form # 组件模块 from django.forms import fields # 字段 from django.forms import widgets # 插件
2、基本语法:
1)使用Form组件的时候,需要在视图函数中,用类去创建组件,然后实例化出这个类的对象,利用Form组件的内部方法,进行数据校验和处理。
a.用类创建form组件,用以匹配不通字段类型的数据 LoginForm(Form) 字段名 = xxxx.xxField() # 本质验证规则,正则表达式 字段名 = xxxx.xxField() # 本质验证规则,正则表达式 字段名 = xxxx.xxField() # 本质验证规则,正则表达式 字段名 = xxxx.xxField() # 本质验证规则,正则表达式 b.实例化一个对象 obj = LoginForm(用户提交的数据) c. 校验 result = obj.is_valid() d.检验成功,获取用户提交的所有数据,字典类型 obj.cleaned_data e.有错误,显示错误信息 obj.errors
Django提供 Form组件: 1. 定义规则 from django.forms import Form # 组件模块 from django.forms import fields # 字段 from django.forms import widgets # 插件 class xxx(Form): #用类去匹配不同数据类型的值 xx = fields.CharField(required=True, max_length= ,min_length=, error_message={ 错误信息,字典类型。最好是以参数为key })#注意:自定义名必须和数据提交的name名一致。 # html标签name属性 = Form类字段名 2. 使用 obj = xxx(request.POST) #实例化出一个对象,接收返回的POST请求内容 # 用于校验数据 v = obj.is_valid() ---> 返回布尔值 True or False # 所有错误信息 obj.errors -----> 错误信息获取的是一个对象,查看发现数据是ul标签 # 正确信息 dic = obj.cleaned_data ---> 字典类型的数据
2)关于错误信息,一般是用参数名为key,values值为页面显示的错误提示信息内容。有两个主要信息:"invalid":"格式错误";“required”:"数据不能为空"。
error_messages={
"required":"内容不能为空",
"invalid":"格式错误,请重新输入!",
"min_length":"数据长度太短",
"max_length":"数据长度太长",
}
3)模版定义,后台将生成的Form组件的对象传递给模版页面,页面通过这个对象可以自动生成HTML标签。
{{ obj.字段名 }} -------> 用于生成标签; {{ obj.errors.字段名.0 }} ------> 用于显示当前字段名的错误信息。
<form action="/" method="POST" enctype="multipart/form-data"> <p>{{ obj.user }} {{ obj.user.errors }}</p> <p>{{ obj.gender }} {{ obj.gender.errors }}</p> <p>{{ obj.email }} {{ obj.city.errors }}</p> <p>{{ obj.pwd }} {{ obj.pwd.errors }}</p> <p><input type="submit" value="提交"/></p> </form>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<form method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ form.xxoo.label }} {{ form.xxoo.id_for_label }} {{ form.xxoo.label_tag }} {{ form.xxoo.errors }} <p>{{ form.user }} {{ form.user.errors }}</p> <input type="submit" /> </form>
当然也有简单的方法,直接把所有的标签生成,但是这种方式远没有上面那种使用起来灵活,所以推荐还是使用灵活方式吧!
{{obj.as_p}} <ul> {{obj.as_ul}} </ul> <table> {{obj.as_table}} </table>
利用Form组件,解决form表单输入内容,页面刷新,数据消失的问题!
利用:Form生成HTML标签,后台处理时: 第一次以GET请求方式访问 - GET obj = From() return render(request,"模版页面",{"obj":obj}) Form生成HTML标签 <input .... /> 第二次以POST方式发来数据时,把post中传回的数据以参数的形式传入定义的Form类中,再返回给页面,这样操作就是给标签写入了默认值。 - POST obj = Form(request.POST) return render(request,"模版页面",{"obj":obj}) Form生成HTML标签 <input . value='xx'/>
注意:
让页面显示默认的初始值参数诧异:
- data ={} 会显示信息,也会做errors校验;
- initial={} 第一次,仅显示信息,不会错误校验;
Form 表单只能生成form表单内的标签或是select标签,其他标签不能生成。
示例代码,建议看完全部文档再说:
1、添加学生
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class StudentForm(Form): sname = fields.CharField( error_messages={ "required": "内容不能为空", "invalid": "格式错误,请重新输入!", }, widget=widgets.TextInput(attrs={'class': 'form-control'}), ) age = fields.IntegerField( min_value=18, max_value=30, error_messages={ "required": "内容不能为空", "invalid": "格式错误,请重新输入!", }, widget=widgets.TextInput(attrs={'class': 'form-control'}), ) email = fields.EmailField(widget=widgets.TextInput(attrs={'class': 'form-control'}),) cls_id = fields.ChoiceField( choices=[], widget=widgets.Select(attrs={'class': 'form-control'},) ) def __init__(self,*args,**kwargs): super(StudentForm,self).__init__(*args,**kwargs) self.fields["cls_id"].choices=models.Classes.objects.values_list("id","cname") def add_student(request): if request.method=="GET": obj = StudentForm() return render(request,"add_student.html",{"obj":obj}) else: obj = StudentForm(request.POST) if obj.is_valid(): models.Student.objects.create(**obj.cleaned_data) return redirect(reverse("s1")) else: return render(request, "add_student.html", {"obj": obj})
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="{% url "add_stu" %}"> {% csrf_token %} <h3>添加学生</h3> <p><span>学生姓名</span>{{ obj.sname }} <span style="font-size: 10px;color: red">{{ obj.errors.sname.0 }}</span></p> <p><span>学生年龄</span>{{ obj.age }} <span style="font-size: 10px;color: red">{{ obj.errors.age.0 }}</span></p> <p><span>邮箱</span>{{ obj.email }} <span style="font-size: 10px;color: red">{{ obj.errors.email.0 }}</span></p> <p><span>班级</span>{{ obj.cls_id }} <span style="font-size: 10px;color: red">{{ obj.errors.cls_id.0 }}</span></p> <p> <input type="submit" value="提交"> </p> </form> </body> </html>
2、编辑老师
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class TeacherForm(Form): tname = fields.CharField( error_messages={ "required": "内容不能为空", "invalid": "格式错误,请重新输入!", }, ) # cids = fields.MultipleChoiceField( # choices=models.Classes.objects.values_list("id","cname"), # widget=widgets.SelectMultiple #多选属性 # ) cids = fields.MultipleChoiceField( widget=widgets.SelectMultiple, #多选属性 ) def __init__(self,*args,**kwargs): super(TeacherForm,self).__init__(*args,**kwargs) self.fields["cids"].choices=models.Classes.objects.values_list("id","cname") def edit_teacher(request,n): if request.method=="GET": row = models.Teacher.objects.filter(id=n).first() class_id = row.c2t.values_list("id") ids_list = list(zip(*class_id))[0] if list(zip(*class_id)) else [] obj = TeacherForm(initial={"tname":row.tname,"cids":ids_list}) return render(request,"edit_teacher.html",{"obj":obj,"n":n}) else: obj = TeacherForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) cids = obj.cleaned_data.pop("cids") print(cids) print(obj.cleaned_data) models.Teacher.objects.filter(id=n).update(**obj.cleaned_data) models.Teacher.objects.filter(id=n).first().c2t.set(cids) return redirect(reverse("t1")) else: return render(request, "edit_teacher.html", {"obj": obj, "n": n})
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="{% url "edit_tea" n %}"> {% csrf_token %} <h3>编辑老师</h3> <p><span>老师姓名:</span> {{ obj.tname }} </p> <p><span>任教班级:</span> {{ obj.cids }} </p> <p> <input type="submit" value="提交"> </p> </form> </body> </html>
Form类执行原理(代码解释):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def login(request): if request.method == 'GET': return render(request,'login.html') else: obj = LoginForm(request.POST) # is_valid """ 1. LoginForm实例化时, self.fields={ 'user': 正则表达式 'pwd': 正则表达式 } 2. 循环self.fields flag = True errors cleaned_data for k,v in self.fields.items(): # 1. user,正则表达式 input_value = request.POST.get(k) 正则表达式和input_value flag = False return flag """ if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,'login.html')
3、Form类
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成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, 是否支持本地化 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类型 ...
PS:关于参数的注意点:
widget=widgets.Select, ******** 用于指定生成怎样的HTML,select,text,input/. label='用户名', # obj.t1.label disabled=False, # 是否可以编辑 label_suffix='--->', # Label内容后缀 initial='666', #在input框中显示的默认值 #不写默认为None;单值时单写一个值;多值时,是字典类型{"字段名":值} help_text='。。。。。。', # 提供帮助信息,给用户写值提供的参考。 choices = [(),(),] #列表类型,里边每个值是元组类型的数据。choices选值的时候是循环整个列表,对每个元组在内部进行匹配及拼接,转成select下拉单中所有的option标签的数据。
2)Django内置插件: 用于定制不同的插件,用于显示不同类型的input标签或是select标签。使用方式:参数widget=widgets.插件名(属性)
PS:属性书写方式:
自定义属性:attr={"属性名":"属性值"} ----------------------> 例如:attr={"class":"c1"},
TextInput(Input) ---->输入文本框 type="text" NumberInput(TextInput) EmailInput(TextInput) URLInput(TextInput) PasswordInput(TextInput) ----->输入密码框 type="password" HiddenInput(TextInput) Textarea(Widget) ----->输入大文本内容框 type="textarea" DateInput(DateTimeBaseInput) DateTimeInput(DateTimeBaseInput) TimeInput(DateTimeBaseInput) CheckboxInput -----> 单个复选框 Select -----> select下拉单 单选属性 NullBooleanSelect SelectMultiple ------> 下拉表单多选属性 RadioSelect ------> 单选框 CheckboxSelectMultiple ------>复选框 FileInput ClearableFileInput MultipleHiddenInput SplitDateTimeWidget SplitHiddenDateTimeWidget SelectDateWidget
常用选择插件
# 单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 # )
关于字段和插件:
常规的像:字符串,数字,邮箱,IP等这些数据类型按照上边操作即可。现在聊一些特殊的多对多:ChoiceField() <可被替代> 和 MultiplChoiceField()类型。
而查用的插件有:CheckBox,radio,input,textarea,File
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class TestForm(Form): t1 = fields.CharField( widget=widgets.Textarea(attrs={}) ) t2 = fields.CharField( widget=widgets.CheckboxInput ) t3 = fields.MultipleChoiceField( choices=[(1,"篮球"),(2,"足球"),(3,"溜溜球")], widget=widgets.CheckboxSelectMultiple )#多选 t4 = fields.ChoiceField( choices=[(1, "篮球"), (2, "足球"), (3, "溜溜球")], widget=widgets.RadioSelect )#单选 def test(request): if request.method =="GET": obj = TestForm(initial={"t3":[1,2]}) #为t3字段传入默认选项 return render(request,"test.html",{"obj":obj})
通过Form组件实现select框
单选:两种形式可用 cls_id = fields.IntegerField( # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')]) #普通的传值 widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'})#获取数据库中的值 ) cls_id = fields.ChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.Select(attrs={'class': 'form-control'}) ) obj = FooForm({'cls_id':1}) #实例化对象,传入默认选项值。 多选 cls_id = fields.MultipleChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.SelectMultiple ) obj = FooForm({'cls_id':[1,2,3]})#实例化对象,传入默认选项值。
在使用选择标签(select框)时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,也就是说数据从数据库拿取之后,在页面上显示;如果此时数据库内容有变更,那么页面就没办法显示变更之后的内容。需要重启服务才能看到。主要原因就是在于不能动态的获取数据库中的数据!那么需要自定义构造方法从而达到此目的。
实现上述动态获取的方式,有两种方法:一种是利用django自带的模块,另一种就是在类中自定义(**推荐**)。
修复页面刷新无法动态显示数据库内容: 方式一:django自带模块,括号内属性为数据库操作(对于大型的数据表,不建议使用这种方式) from django.forms import models as form_model class TeacherForm(Form): tname = fields.CharField(min_length=2) xx = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all()) ----> 用于多选 xx = form_model.ModelChoiceField(queryset=models.Classes.objects.all()) -----> 用于单选 方式二:根据Form组件的运行原理,把方法属性中取值的操作放到函数属性中,及类初始化的__init__中,这样每实例化一次对象就会动态获取一次。 - 多选操作: class TeacherForm(Form): tname = fields.CharField(min_length=2) xx = fields.MultipleChoiceField( widget=widgets.SelectMultiple ) def __init__(self,*args,**kwargs): super(TeacherForm,self).__init__(*args,**kwargs) self.fields['xx'].choices = models.Classes.objects.values_list('id','title') -单选操作: class StudentForm(Form): sname = fields.CharField() .... cls_id = fields.ChoiceField(widget=widgets.Select) def __init__(self,*args,**kwargs) super(StudentForm,self).__init__(*args,**kwargs) self.fields["cls_id"].choices=models.Classes.objects.values_list('id','title')
注意点:
1、Django内部提供的两个关于从数据库动态获取数据的字段方法:ModelChoiceField(单选),ModelMultipleChoiceField(复选),其中ModelMultipleChoiceField类继承的是ModelChoiceField类。引入方法: from django.forms.models import ModelChoiceField,ModelMultipleChoiceField
这两个类内,都有 QuerySet 这个形参,意思就是从获取数据库的数据(Django ORM 从数据库获取的数据类型就是QuerySet),这样就能动态获取数据。但是这两个字段需要依赖数据库中写入的__str__方法,才可以正常显示数据对象,如果不写给出的就是对应models类的objects对象!
2、还是推荐在类中重写__init__ 的方法,他继承父类的__init__,然后在这个方法中对某个字段{self.fields["字段名"]}加上 choices=获取数据库数据方法 ,这样就不仅能做到动态获取数据库的数据,同时也不需要对应的class表内写__str__方法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class TestForm(Form): t1 = fields.CharField( widget=widgets.Textarea(attrs={}) ) t2 = fields.CharField( widget=widgets.CheckboxInput ) t3 = fields.MultipleChoiceField( choices=[(1,"篮球"),(2,"足球"),(3,"溜溜球")], widget=widgets.CheckboxSelectMultiple )#多选 t4 = fields.ChoiceField( choices=[(1, "篮球"), (2, "足球"), (3, "溜溜球")], widget=widgets.RadioSelect )#单选 def test(request): if request.method =="GET": obj = TestForm(initial={"t3":[1,2]}) return render(request,"test.html",{"obj":obj})
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action=""> <p> {{ obj.t1 }} </p> <p> {{ obj.t2 }} </p> <p> {{ obj.t3 }} </p> <p> {{ obj.t4 }} </p> </form> </body> </html>
三、自定义验证规则
在数据校验的过程中,及obj.is_valid() 会对传入的数据按照自己字段的正则规则进行校验。当我们添加额外正则或是校验方法的时候,校验时除了字段及内置的方法,也会按照我们定义的正则去判断。
1、自定义额外的正则表达式,不写默认字段自己的规则;写就需要导入相对应的正则模块。
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator #导入正则验证模块 -----> 语法RegexValidator["正则规则","匹配错误的错误信息"]
class MyForm(Form): user = fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],)
2、自定义组件:
请求来时,会有正则匹配;数据提交之后,内部会进行循环字段校验。
可以通过is_vilid()查看匹配规则的源代码,并能熟知校验流程
data 和inital 差别就在于 is_bound 的值是True or False
is_bound 为True就会把数据获取到进行校验。
errors 用于错误验证。其下 self.full_clean 校验
field.clean 正则校验每次的字段值
- clean_字段,必须返回值
- clean()
有返回值:cleaned_data = 返回值
无返回值:cleaned_data = 原来的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
自定义扩展错误信息校验方法!!! class TestForm(Form): user = fields.CharField() pwd = fields.CharField() 可以自定义一个字段的扩展函数,用于除正则外的其他验证 。 验证成功后,后台会给对应的字段重新赋值。所以不管验证成功与否,必须有返回值。 需要改值就把自定义验证的结果返回,不变的话就返回原值。 例如:数据库校验。 执行顺序:先找一个字段,然后再找改字段的自定义函数。周而复始 如果字段验证都没有成功,就会抛异常,不再执行扩展函数。 from django.core.exceptions import ValidationError 错误信息 def clean_user(self): ----->自处理只取自己的值 v = self.cleaned_data["user"] ----> 执行前已经完成赋值,有值! if models.Student.objects.filter(name = v).count() raise ValidationError("用户名已经存在",code="定制错误信息的key") 不写默认是:invalid return self.cleaned_data["user"] def clean(self): ---> 纯正的用来自定义功能的函数 - 此时所有的字段都已经过滤完,才来执行此操作,对整体的数据做验证。 操作举例: 对数据库有个联合唯一的判断,单值存在没问题,同时存在则报错。 user = self.cleaned_data.get("user") email = self.cleaned_data.get("email") if models.Student.objects.filter(user=user,email=email).count() raise ValidationError("数据错误,用户名和邮箱联合存在!") return self.cleaned_data ---> 不做任何操作,直接默认写即可
四、文件上传
Form组件,也可以用于上传文件。
后台接收POST方式提交的请求时,需要用类的不同参数去接收不同数据。
data = request.POST 仅是普通文本类型的数据
files = request.FILES 文件数据
obj.cleaned_data 获取的是字典类型的数据,其中文件名对应的值还是一个文件对象。
文件上传注意点及整理:
前端form表单中需要增加一条属性 enctype="multipart/form-data" 与上传文件协议有关。 ----> 封装的是文件的数据
后台通过 request.FILES 获取用户上传的文件,直接获取的是一个文件对象。
file_obj = request.FILES.get("名字") ----> 获取的是文件对象
print(type(file_obj)) 查看文件对象类型
file_obj.name 文件名字
file_obj.size 文件大小 以字节为单位
file_obj.chunks() 以块为单位,获取文件的内容。 ----> django提供的.chunks()方法。
代码举例:
1、普通页面标签文件上传的处理方式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/f1/" enctype="multipart/form-data"> {% csrf_token %} <p> <span>留言</span> <input name="lag" type="text"> </p> <p> <span>文件</span><input name="files" type="file"> </p> <p> <input type="submit" value="提交"> </p> </form> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def f1(request): if request.method=="GET": return render(request,"f1.html") else: print(request.POST.get("lag")) #查看接收的内容 print(request.FILES.get("files").name) #查看文件名字 print(request.FILES.get("files").size) #查看文件大小 import os file_obj = request.FILES.get("files") #找一个滤镜 f = open(os.path.join("static",file_obj.name),"wb") #把上传的文件存放到某个路径下 for chunk in file_obj.chunks(): f.write(chunk) f.close() return render(request,"f1.html")
2、Form组件的处理方式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="POST" action="/f2/" enctype="multipart/form-data"> {% csrf_token %} <p> <span>留言</span>{{ obj.user }} </p> <p> <span>文件</span> {{ obj.files }} </p> <p> <input type="submit" value="提交"> </p> </form> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class F2Form(Form): user = fields.CharField() files = fields.FileField() #文件对象,不再是字符串或是数字。 def f2(request): if request.method=="GET": obj = F2Form() return render(request,"f2.html",{"obj":obj}) else: obj = F2Form(data=request.POST,files=request.FILES) if obj.is_valid(): print(obj.cleaned_data) print(obj.cleaned_data.get("files").name) print(obj.cleaned_data.get("files").size) return render(request,"f2.html",{"obj":obj})
====练习====
Form组件应用:Form组件和ORM数据库操作结合,实现对学生表,班级表,老师表,及关系表的增删改查操作!
练习相关代码,Django框架实现。猛击下载!
学习参考地址:
solo大神手笔 ------->主要是有动图