django框架提供了一個form類,來處理web開發中的表單相關事項。衆所周知,form最常作的是對用戶輸入的內容進行驗證,爲此django的forms類提供了全面的內容驗證和保留用戶上次輸入數據的支持。php
form組件有2大大功能css
對用戶提交的內容進行驗證(form表單/Ajax)html
保留用戶上次輸入的內容前端
一、對用戶提交的數據進行驗證python
form組件驗證的原理web
1.obj=Form()form組件類實例化時找到類中全部的字段 把這些字段 變成組合成字典;ajax
self.fields={‘user’:正則表達式1,‘pwd’:正則表達式2}正則表達式
2.循環self.fields字典(本身寫的字段)數據庫
for k,v in self.fields.items():django
K是user,pwd
v是正則表達式
3.每次循環經過self.fields字典的鍵, 一個一個的去get前端POST提交的數據 獲得用戶輸入數據;
input_value= request.post.get(‘k’)(因此form字段的名稱,要和前端的name屬性匹配)
4.每次拿到用戶輸入的數據 (input_value)和進行正則表達式匹配;
5.匹配成功flag=True 匹配失敗flag=falsh,最後 return flag obj.is_valid=flag。
若是For自帶的規則和正則知足不了驗證需求,可在Form類中自定義方法,作擴展。
6.每一個字段驗證經過後,每一個字段執執行self.clean_filelds函數(自定義 對Form類中的字段作單獨驗證,好比去數據庫查詢判斷一下用戶提交的數據是否存在?)
7. 執行Form組件的clean_form方法進行總體驗證!(既然每一個字段都驗證了,就能夠對用戶提交的數據作總體驗證了!好比進行聯合惟一的驗證)
8.最後執行相似 clean_form的post_clean方法結束驗證。(通常不使用post_clean作自定義過濾,clean_form方法徹底能夠解決)
form表單提交驗證(form表單(會發起 get)提交刷新失去上次內容)
from django.shortcuts import render,HttpResponse,redirect from django.forms import Form from django.forms import fields class Login(Form): #from驗證規則 用戶名 6-10字符 required不能爲空 name=fields.CharField(max_length=10, min_length=6, required=True, error_messages={ 'required':'用戶名不能爲空', #error_messages參數 自定義錯誤信息 'min_length':'過短了', 'max_length': "太長了", } ) # z注意name 必須和 from表單提交的一致,要麼二則怎麼對比校驗呢 pwd= fields.CharField(min_length=3, required=True, error_messages={ 'required': '密碼不能爲空', # error_messages參數 自定義錯誤信息 'min_length': '過短了', 'max_length': "太長了", } ) def index(request): if request.method=='GET': return render(request,'login.html') else: obj=Login(request.POST) #把客戶端提交來的form表單和 和匹配規則放在一塊兒 res=obj.is_valid() #自動校驗 給出結果 True 或者 False if res: #驗證成功後obj.cleaned_data獲取成功的數據,字典類型正好對應數據 的批量操做 print(obj.cleaned_data) return redirect('http://www.baidu.com') #obj.errors獲取錯誤信息(對象類型)就能夠傳到前端顯示了! else: return render(request,'login.html',{'obj':obj})
Aja提交驗證(不會刷新,上次輸入內容自動保留)
Django的form驗證功能不只限於對form表單提交的數據驗證,一樣適用於ajax提交方式;
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>ajx提交</title> </head> <body> <form method="post" action="/aja_login/" id="f1"> {%csrf_token%} <p>用戶:<input type="text" name="name"></p> <p>密碼:<input type="password" name="pwd"></p> <p><input type="button" onclick="Ajxform()" value="aja提交"></p> </form> </body> <script src="/static/zhanggen.js"></script> <script> function Ajxform(){ $('.c1').remove() $.ajax({ url:'/alogin/', type:'POST', dataType:'JSON', data:$('#f1').serialize(), success:function (args) { if (args.status){ } else{ {# {status: false, msg: Object}#} {# console.log(args);#} {# Jquery循環服務端 傳過來的 錯誤信息對象#} $.each(args.msg,function (index,value) { console.log(index,value); {# index----> name ["過短了"]#} {# value-----pwd["密碼不能爲空"]#} var tag=document.createElement('span'); tag.innerHTML= value[0]; tag.className='c1'; console.log(index); {# 尋找input下 屬性爲 name 和pwd的標籤(字符串拼接) 在他們後半加 上tag標籤也就是錯誤 信息 #} $('#f1').find('input[name="'+ index +'"]').after(tag) }) } }})} </script> </html>
Views
from django.shortcuts import render,HttpResponse,redirect from django.forms import Form from django.forms import fields import json class Login(Form): #from驗證規則 用戶名 6-10字符 required不能爲空 name=fields.CharField(max_length=10, min_length=6, required=True, error_messages={ 'required':'用戶名不能爲空', #error_messages參數 自定義錯誤信息 'min_length':'過短了', 'max_length': "太長了", } ) # z注意name 必須和 from表單提交的一致,要麼二則怎麼對比校驗呢 pwd= fields.CharField(min_length=3, required=True, error_messages={ 'required': '密碼不能爲空', # error_messages參數 自定義錯誤信息 'min_length': '過短了', 'max_length': "太長了",}) def agx_login(request): ret={'status':True,'msg':None} if request.method=='GET': return render(request,'ajalogin.html') else: obj=Login(request.POST) ret['status']=False ret['msg']=obj.errors return HttpResponse(json.dumps(ret))
自定義正則表達式驗證
密碼修改實例
password_complexity='^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).+$' #密碼複雜性要求:密碼必須包含數字、大、寫字母
{% extends "arya/layout.html" %} {% block out_css %} <link rel="stylesheet" href="/arya/static/arya/css/form-control.css"/> {% endblock %} {% block content %} {% csrf_token %} <div> <div class="row"> <a class="btn btn-default" href="{{ request.META.HTTP_REFERER }}">返回</a> <div class="col-md-5 col-md-offset-3"> <form> <div class="form-group"> <label for="exampleInputPassword1">請輸入原始密碼</label> <input name="old_pwd" type="password" class="form-control" id="exampleInputPassword0" placeholder="原始密碼"> </div> <div class="form-group"> <label for="exampleInputPassword1">新的密碼</label> <input name="first_new_pwd" type="password" class="form-control" id="exampleInputPassword1" placeholder="新的密碼"> </div> <div class="form-group"> <label for="exampleInputPassword1">再次輸入新密碼</label> <input name="second_new_pwd" type="password" class="form-control" id="exampleInputPassword2" placeholder="再次輸入新密碼"> </div> <button id="submit_pwd" type="button" class="btn btn-default">提交</button> </form> <br> <p style="color: red" id="__all__" class="error_msg"></p> <br> <p>設置密碼時請符合如下規則:最小長度八、包含大小寫英文字母、數字。</p> </div> </div> </div> {# </section>#} <script> $('#submit_pwd').click(function () { var $csrf = $("[name='csrfmiddlewaretoken']").val(); var $old_pwd = $('[name="old_pwd"]').val(); var $first_new_pwd = $('[name="first_new_pwd"]').val(); var $second_new_pwd = $('[name="second_new_pwd"]').val(); var pwd_formdata = new FormData(); pwd_formdata.append('csrfmiddlewaretoken', $csrf); pwd_formdata.append('old_pwd', $old_pwd); pwd_formdata.append('first_new_pwd', $first_new_pwd); pwd_formdata.append('second_new_pwd', $second_new_pwd); $.ajax({ type: 'post', data: pwd_formdata, processData: false, contentType: false, success: function (data) { var response = JSON.parse(data); var $error_p = $('<p class="error_msg" style="color:red">'+ '</p>'); if (response.status == 200) { window.location = "/login/" } if (response.status == 404) { var error_p =$error_p.text(response.msg.old_pwd); $('[name="old_pwd"]').after(error_p) } else { $.each(response.msg, function (k, v) { var $input_tag = $("[name=" + k +"]"); var error_p = '<p style="color: red" class="error_msg">'+v[0]+'</p>'; $input_tag.after(error_p); console.log(response.msg); if (k == '__all__') { $('#__all__').text(v[0]) } }); } setTimeout("$('.error_msg').remove()", 3000); } }) }) </script> {% endblock %}
def changepwd(request): #修改密碼視圖 if request.method=='POST': current_user = request.session.get('username') response_info = {'status':200,'msg':None} user_db_obj = models.UserInfo.objects.filter(username=current_user).first() if not user_db_obj or user_db_obj.password != make_md5(request.POST.get('old_pwd').strip()): response_info['status'] = 404 response_info['msg'] = {'old_pwd': '原始密碼錯誤.'} else: obj = FormCheck.Set_password(request.POST) if not obj.is_valid(): response_info['status'] = 403 response_info['msg']=obj.errors else: user_db_obj.password = make_md5(request.POST.get('second_new_pwd').strip()) user_db_obj.save() return HttpResponse(json.dumps(response_info, ensure_ascii=False)) return render(request,'woke_order/changepwd.html')
from django.forms import Form from django.forms import fields from django.forms import widgets from cmdb.models import * import re from django.core.exceptions import ValidationError class Set_password(Form): password_complexity='^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).+$' old_pwd=fields.RegexField(password_complexity, required=True, min_length=8, error_messages={'invalid':'不知足密碼複雜性要求','required':'密碼不能爲空!','min_length':'密碼長度少於8位'}, ) first_new_pwd=fields.RegexField(password_complexity, required=True, min_length=8, error_messages={'invalid':'不知足密碼複雜性要求','required':'密碼不能爲空!','min_length':'密碼長度少於8位'}, ) second_new_pwd =fields.RegexField(password_complexity, required=True, min_length=8, error_messages={'invalid':'不知足密碼複雜性要求','required':'密碼不能爲空!','min_length':'密碼長度少於8位'}, ) def clean(self): first_new_pwd=self.cleaned_data.get('first_new_pwd') second_new_pwd=self.cleaned_data.get('second_new_pwd') if first_new_pwd and second_new_pwd: if first_new_pwd.strip() == second_new_pwd.strip(): return self.cleaned_data raise ValidationError("兩次密碼不一致")
IP 和端口
ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$" port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$'
from django.forms import Form,fields from django.forms import widgets,forms import re ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$" port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$' class dbinfo_create(Form): data_mode_type=fields.CharField(required=True,error_messages={'required':'數據庫模型不能爲空.'}) database_type=fields.CharField(required=True,error_messages={'required':'數據庫類型不能爲空'}) host=fields.RegexField(ipaddr_validate,required=True,error_messages={'required':'IP不能爲空','invalid':'不合法的IP地址'}) port=fields.RegexField(port_validate,required=True,error_messages={'required':'端口不能爲空.','invalid':'端口無效'}) # instance_nikename=fields.CharField(max_length=20,error_messages={'required':'端口不能爲空.',"max_length":"標題不能超過20個字"}) db_business=fields.CharField(required=True,error_messages={'required':'請說明所屬業務線'}) DBA=fields.CharField(required=True,error_messages={'required':'請說明DBA'}) responsible_person=fields.CharField(required=True, error_messages={'required':'請選擇相關責任人!'})
二、動態生成HTML標籤,保留用戶上次輸入的內容。
如何保留用戶上次輸入的內容?
因爲form表單submit以後(發送post請求) 數據提交到 後端,無論前端輸入的數據是否正確,服務端也要響應,因此頁面會刷新;
因此沒法保留用戶上次輸入的內容;如何解決呢?
一、把定義的定義的Form類,實例化(obj=Login() )內部調用一個__str__的方法,若是沒有傳值 返回<input type="text" name=「字段」>name='字段名空的input標籤
二、把這個實例化以後的對象傳到前端顯示,讓用戶輸入值;用戶輸入值經過post方法提交到後臺。
三、若是後臺實例化一個對象 obj=Login(request.POST)傳入了值, <input type="text" name=「字段」 value='request.post的數據'>而後後端再返回客戶端就能夠看到用戶輸入的值了!
保留用戶上次輸入的內容 是利用了 obj=Login(request.POST)接收了用戶輸入的值
視圖
from django import forms class Myform(forms.Form): #一、寫1個繼承forms.Form的類,定製form表單的數據類型; user=forms.CharField(max_length=32,min_length=3,label='用戶名', error_messages={'required':'不能爲空'}, widget=forms.TextInput(attrs={'class':'sb','placeholder':'用戶名'},) ) age=forms.IntegerField(label='年齡',error_messages={'required':'不能爲空'},) email=forms.EmailField(label='郵箱',error_messages={'required':'不能爲空'},) def register2(request): if request.method=='GET': forms_obj=Myform() #二、實例化類,把對象渲染到模板 return render(request,'form——register.html',{'forms_obj':forms_obj}) else: forms_obj=Myform(request.POST) #三、把提交的數據封裝成form對象 if forms_obj.is_valid(): #四、使用 form對象的.is_valid()方法,校驗提交過來的數據是否符合驗證規則 data=forms_obj.cleaned_data #五、獲取驗證經過的數據(字典類型,可直接 **dict插入數據庫) # User.objects.create_user(**data) return HttpResponse('OK') else: print(forms_obj.cleaned_data) #六、因爲用戶在form表單提交了值,利用這一點, # 把forms_obj=Myform(request.POST)渲染到前端就能夠保存用戶輸入的值 return render(request, 'form——register.html', {'forms_obj':forms_obj})
前臺
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Django_form驗證</title> </head> <body> <form action="/form/" method="post" novalidate> {% csrf_token %} <p>用戶:{{ forms_obj.user }}{{ forms_obj.errors.user.0 }}</p> <p>年齡:{{ forms_obj.age }}{{ forms_obj.errors.age.0}}</p> <p>郵箱:{{ forms_obj.email }}{{ forms_obj.errors.email.0}}</p> <button>提交</button> </form> </body> </html>
from django.shortcuts import render,HttpResponse,redirect from django.forms import Form from django.forms import fields import json class Login(Form): #from驗證規則 用戶名 6-10字符 required不能爲空 name=fields.CharField(max_length=10, min_length=6, required=True, error_messages={ 'required':'用戶名不能爲空', #error_messages參數 自定義錯誤信息 'min_length':'過短了', 'max_length': "太長了", } ) # z注意name 必須和 from表單提交的一致,要麼二則怎麼對比校驗呢 pwd= fields.CharField(min_length=3, required=True, error_messages={ 'required': '密碼不能爲空', # error_messages參數 自定義錯誤信息 'min_length': '過短了', 'max_length': "太長了",}) def index(request): ret={'status':True,'msg':None} if request.method=='GET': obj=Login() #自動生成空白的input標籤 發送給客戶端) return render(request,'login.html',{'obj':obj}) else: obj=Login(request.POST) #把客戶端提交來的form表單和 和匹配規則放在一 res=obj.is_valid() #自動生成空白的input標籤 發送 if res: #驗證成功後obj.cleaned_data獲取成功的數據,字典類型正好對應數據 的批量操做 return HttpResponse('OK') #obj.errors獲取錯誤信息(對象類型)就能夠傳到前端顯示了! else: return render(request,'login.html',{'obj':obj})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陸頁面</title> </head> <body> <form method="post" action="/login/" id="f1" novalidate > {%csrf_token%} <h1>用戶登陸</h1> <p>用戶名 {{obj.name}}{{ obj.name.errors.0}}</p> <p>密碼:{{ obj.pwd}}{{ obj.pwd.errors.0}}</p> <p><input type="submit" value="登陸"></p> </form> </body> </html>
三、承上啓下 form組件的套路(執行流程):
(1)在後端定義類和字段,實例化Form類;
(2)到用戶 發送get請求時,服務端渲染到模板(空標籤/默認值)發送到客戶端顯示
(3)客戶端填數據,POST提交到後端;
(4)後端驗證,返回結果給前端;(切記Form組件是在後端生成,發送給客戶端顯示,客戶端填完數據在發回服務端!)
一、導入Form插件
from django.forms import Form,fields
from django.forms import widgets
class Class_form(Form): title=fields.RegexField('全棧\d+', # initial='全棧', #設置input標籤中的默認值 min_length=2, required=True, error_messages={'invalid':"必須以全棧開頭", 'min_length':'過短了', 'required':"不能爲空", } ) class Students(Form): name=fields.CharField(required=True, widget=widgets.TextInput(attrs={'class':'form-control'}), error_messages={'required':'姓名不能爲空'}) sex=fields.CharField(required=True,error_messages={'required':'不能爲空'}, widget = widgets.TextInput(attrs={'class': 'form-control'}) ) cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'), attrs = {'class': 'form-control'} ), ) class teacher_form(Form): tname=fields.CharField(required=True,error_messages={'required':"姓名不能爲空"} ) # classes=fields.CharField( #多選 不能用這個插件不使用request.post.getlist()取值 # widget=widgets.Select(choices=models.Classes.objects.values_list(), # attrs={'multiple':'multiple'}) # ) # classes=fields.CharField(widget=widgets.SelectMultiple( # # 若是在fields.CharField字段該插件,獲得會是{'tname': 'ww', 'classes': "['2', '3']"}字符串 # choices= models.Classes.objects.values_list('id','title')) ) classes=fields.MultipleChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.SelectMultiple,error_messages={'required':'選擇不能爲空'}) def __int__(self,*args,**kwargs): #解決數據不一樣步的bug,每次form組件實例化時 都從新去數據庫拿數據 super(teacher_form,self).__init__(*args,**kwargs) self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title') class Test_form(Form): name=fields.CharField() #動態生成 text類型的input標籤 text=fields.CharField(widget=widgets.Textarea,) #動態生成文本框 age=fields.CharField(widget=widgets.CheckboxInput) #動態生成單選框 holby=fields.MultipleChoiceField( #MultipleChoiceField動態生成複選框 choices=[(1,'籃球'),(2,"足球"),(3,"高俅")], widget=widgets.CheckboxSelectMultiple) sex=fields.MultipleChoiceField( choices=[(1,'男'),(2,'女')], widget=widgets.RadioSelect ) select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐縣')]) #經過ChoiceField字段動態生成單選框 # 經過form組件的MultipleChoiceField字段 mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'萬寶利') ], widget=widgets.SelectMultiple) file=fields.FileField() #經過form組件的.FileField動態生成 文件上傳input標籤,注意在提交到後臺是對象
二、定義類和字段(驗證規則) 擴展方法
class Form_login(Form):
字段 參數
user=fields.RegexField(正則表達式,驗證規則,error_messages={錯誤信息},widget=html標籤插件,attrs = {'標籤插件的屬性'})
Django的form在obj.is_valid()方法內提供2個鉤子函數,以便咱們隨時調用他自定製一些複雜的驗證規則;
局部鉤子函數
class Class_form(Form): title=fields.RegexField('全棧\d+', # initial='全棧', #設置input標籤中的默認值 min_length=2, required=True, error_messages={'invalid':"必須以全棧開頭", 'min_length':'過短了', 'required':"不能爲空", } ) class Students(Form): name=fields.CharField(required=True, widget=widgets.TextInput(attrs={'class':'form-control'}), error_messages={'required':'姓名不能爲空'}) sex=fields.CharField(required=True,error_messages={'required':'不能爲空'}, widget = widgets.TextInput(attrs={'class': 'form-control'}) ) cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'), attrs = {'class': 'form-control'} ), ) class teacher_form(Form): tname=fields.CharField(required=True,error_messages={'required':"姓名不能爲空"} ) # classes=fields.CharField( #多選 不能用這個插件不使用request.post.getlist()取值 # widget=widgets.Select(choices=models.Classes.objects.values_list(), # attrs={'multiple':'multiple'}) # ) # classes=fields.CharField(widget=widgets.SelectMultiple( # # 若是在fields.CharField字段該插件,獲得會是{'tname': 'ww', 'classes': "['2', '3']"}字符串 # choices= models.Classes.objects.values_list('id','title')) ) classes=fields.MultipleChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.SelectMultiple,error_messages={'required':'選擇不能爲空'}) def __int__(self,*args,**kwargs): #解決數據不一樣步的bug,每次form組件實例化時 都從新去數據庫拿數據 super(teacher_form,self).__init__(*args,**kwargs) self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title') class Test_form(Form): name=fields.CharField() #動態生成 text類型的input標籤 text=fields.CharField(widget=widgets.Textarea,) #動態生成文本框 age=fields.CharField(widget=widgets.CheckboxInput) #動態生成單選框 holby=fields.MultipleChoiceField( #MultipleChoiceField動態生成複選框 choices=[(1,'籃球'),(2,"足球"),(3,"高俅")], widget=widgets.CheckboxSelectMultiple) sex=fields.MultipleChoiceField( choices=[(1,'男'),(2,'女')], widget=widgets.RadioSelect ) select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐縣')]) #經過ChoiceField字段動態生成單選框 # 經過form組件的MultipleChoiceField字段 mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'萬寶利') ], widget=widgets.SelectMultiple) file=fields.FileField() #經過form組件的.FileField動態生成 文件上傳input標籤,注意在提交到後臺是對象
全局鉤子函數
若是要想要同時對2個form字段進行驗證,就須要全局鉤子函數(應用 驗證2次輸入的密碼是否一致),能夠調用他們自定製複雜的form驗證規則,
問題1: 註冊頁面輸入爲空,報錯:keyError:找不到password
def clean(self): print("---",self.cleaned_data) # if self.cleaned_data["password"]==self.cleaned_data["repeat_password"]: # 報錯緣由:self.cleaned_data是乾淨數據,若是頁面沒有輸入內容,則self.cleaned_data沒有password。 改以下: if self.cleaned_data.get("password")==self.cleaned_data.get("repeat_password"): return self.cleaned_data else: raise ValidationError("兩次密碼不一致")
1.簡單粗暴型
注意:模板語言{{form_obj.as_p}},必定要在設置lable參數
class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='郵箱') pary=fields.ChoiceField(choices=[(1,"技術部"),(2,'銷售部'),(3,'市場部'),],label='部門')
{{obj.as_p}}以P標籤的形式所有顯示 <table> 注意加table標籤形式所有顯示 {{obj.as_table}} </table> <ul>注意加ul標籤形式所有顯示 {{obj.as_table}} </ul>
2.靈活定製型
<p>姓名:{{ obj.name }}</p> <p>性別:{{ obj.sex }}</p> <p>愛好: {{ obj.holby }}</p> <p>婚姻情況:{{ obj.age }}</p> <p>我的簡介 {{ obj.text }}</p> <p>工做地點: {{ obj.select }}</p> <p>居住地點:{{ obj.mselect }}</p> <p>資料上傳:{{obj.file}}</p>
數據校驗
obj=classForm_login(request.POST )
默認校驗:obj=classForm_login(data={} ) 含有錯誤信息: obj=Class_form(initial={'title':class_obj.title})只有html標籤
obj.is_valid() 獲取校驗結果
obj.errors獲取錯誤信息
obj.cleand_data 獲取正確的數據
路由
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^classes/',views.classes), url(r'^classes_add(\d+)/',views.classes_add), url(r'^classes_edit(\d+)/',views.classes_edit), url(r'^students/',views.students), url(r'^students_add(\d+)/',views.students_add), url(r'^students.edit(\d+)/',views.students_edit), url(r'^teachers/',views.teacher), url(r'^teacher_add/',views.teacher_add), url(r'^teacher_edit(\d+)/',views.teacher_edit), url(r'^test/',views.test), ]
視圖
def classes(request): if request.method=='GET': c_list=models.Classes.objects.all() return render(request,'class_list.html',{'clist':c_list}) def classes_add(request,args): if request.method=='GET': obj=Class_form() return render(request,'class_add.html',{'obj':obj}) else: obj=Class_form(request.POST) if obj.is_valid(): models.Classes.objects.create(**obj.cleaned_data ) return redirect('/classes/') else: print('NI') return render(request,'class_add.html',{'obj':obj}) def classes_edit(request,args): if request.method=='GET': class_obj=models.Classes.objects.filter(id=args).first() obj=Class_form(initial={'title':class_obj.title}) return render(request,'class_edit.html',{'obj':obj,'nid':args}) else: obj = Class_form(request.POST) if obj.is_valid(): models.Classes.objects.filter(id=args).update(**obj.cleaned_data) return redirect('/classes/') else: return render(request, 'class_edit.html', {'obj': obj, 'nid': args}) def students(request): if request.method=='GET': students=models.Student.objects.all() return render(request,'students.html',{'student':students}) def students_add(request,nid): if request.method=='GET': obj=Students() return render(request,'student_add.html',{'obj':obj,'nid':nid}) else: obj=Students(request.POST) if obj.is_valid(): models.Student.objects.create(**obj.cleaned_data) return redirect('/students/') else: return render(request,'student_add.html',{'obj':obj,'nid':nid}) def students_edit(request,nid): if request.method=='GET': # print(models.Student.objects.filter(id=nid).values('name','sex','cls_id').first()) obj=Students(initial=models.Student.objects.filter(id=nid).values('name','sex','cls_id').first()) return render(request,'student_edit.html',{'obj':obj,'nid':nid}) else: obj1=Students(request.POST) if obj1.is_valid(): models.Student.objects.filter(id=nid).update(**obj1.cleaned_data) return redirect('/students/') else: return render(request, 'student_edit.html', {'obj': obj1, 'nid': nid}) def teacher(request): if request.method=='GET': teacher_list=models.teacher.objects.all() return render(request,'teacher_list.html',{'tlist':teacher_list}) def teacher_add(request): if request.method=='GET': print(request.method) obj=teacher_form() return render(request,'teacher_add.html',{'obj':obj}) else: obj=teacher_form(request.POST) if obj.is_valid(): classe_ids=obj.cleaned_data.pop('classes') #老師任教的班級 tname=obj.cleaned_data #老師的姓名 create_teacher=models.teacher.objects.create(**tname) create_teacher.c2t.add(*classe_ids) return redirect('/teachers/') else: return render(request,'teacher_add.html',{'obj':obj}) def teacher_edit(request,arg): if request.method=='GET': teacher_obj=models.teacher.objects.filter(id=arg).first() classes_ids=teacher_obj.c2t.values_list('id') cid_list=list(zip(*classes_ids))[0] if list(zip(*classes_ids)) else[] # print(cid_list) 元組 obj=teacher_form(initial={'tname':teacher_obj.tname,'classes':cid_list}) return render(request,'teacher_edit.html',{'obj':obj,'nid':arg} ) else: obj=teacher_form(request.POST) if obj.is_valid(): teacher_name=obj.cleaned_data.pop('tname') models.teacher.objects.filter(id=arg).update(tname=teacher_name) obj1=models.teacher.objects.filter(id=arg).first() classes=obj.cleaned_data.pop('classes') obj1.c2t.set(classes) return redirect('/teachers/') else: return render(request,'teacher_add.html',{'obj':obj}) def test(request): if request.method=='GET': obj=Test_form(initial={'holby':[ 1,2],'name':'張根','text':'我來自太行山','sex':1}) # # print(models.Student.cls.objects.values_list()) # obj=models.Student.objects.filter(id=1).first() # print(obj.cls.title) return render(request,'test.html',{'obj':obj})
form組件
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from django.forms import Form,fields
from django.forms import widgets
class Class_form(Form):
title=fields.RegexField('全棧\d+',
# initial='全棧', #設置input標籤中的默認值
min_length=2,
required=True,
error_messages={'invalid':"必須以全棧開頭",
'min_length':'過短了',
'required':"不能爲空",
}
)
class Students(Form):
name=fields.CharField(required=True,
widget=widgets.TextInput(attrs={'class':'form-control'}),
error_messages={'required':'姓名不能爲空'})
sex=fields.CharField(required=True,error_messages={'required':'不能爲空'},
widget = widgets.TextInput(attrs={'class': 'form-control'})
)
cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
attrs = {'class': 'form-control'}
),
)
class teacher_form(Form):
tname=fields.CharField(required=True,error_messages={'required':"姓名不能爲空"} )
# classes=fields.CharField( #多選 不能用這個插件不使用request.post.getlist()取值
# widget=widgets.Select(choices=models.Classes.objects.values_list(),
# attrs={'multiple':'multiple'})
# )
# classes=fields.CharField(widget=widgets.SelectMultiple(
# # 若是在fields.CharField字段該插件,獲得會是{'tname': 'ww', 'classes': "['2', '3']"}字符串
# choices= models.Classes.objects.values_list('id','title')) )
classes=fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id','title'),
widget=widgets.SelectMultiple,error_messages={'required':'選擇不能爲空'})
def __int__(self,*args,**kwargs): #解決數據不一樣步的bug,每次form組件實例化時 都從新去數據庫拿數據
super(teacher_form,self).__init__(*args,**kwargs)
self.fields['classes'].choices=models.Classes.objects.values_list('id','title')
class Test_form(Form):
name=fields.CharField() #動態生成 text類型的input標籤
text=fields.CharField(widget=widgets.Textarea,) #動態生成文本框
age=fields.CharField(widget=widgets.CheckboxInput) #動態生成單選框
holby=fields.MultipleChoiceField( #MultipleChoiceField動態生成複選框
choices=[(1,'籃球'),(2,"足球"),(3,"高俅")],
widget=widgets.CheckboxSelectMultiple)
sex=fields.MultipleChoiceField(
choices=[(1,'男'),(2,'女')],
widget=widgets.RadioSelect
)
select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐縣')]) #經過ChoiceField字段動態生成單選框
# 經過form組件的MultipleChoiceField字段
mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'萬寶利') ],
widget=widgets.SelectMultiple)
file=fields.FileField() #經過form組件的.FileField動態生成 文件上傳input標籤,注意在提交到後臺是對象
模板
班級管理(1對1)
班級顯示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>班級列表</title> <link rel="stylesheet" href="/static/bootstrap-3.3.5-dist/css/bootstrap.css"> </head> <body> <div style="width: 500px;margin: 0 auto"> <h1>班級列表</h1> <ul> {% for obj in clist %} <li>{{ obj.title }} <a href="/classes_add{{ obj.id }}/">添加</a> <a href="/classes_edit{{ obj.id }}/">編輯</a> </li> {% endfor %} </ul> </div> </body> </html>
添加班級
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加班級</title> </head> <body> <h1>添加班級</h1> <form action='/classes_add{{2}}/' method="post" novalidate> {% csrf_token %} <p>班級添加:{{ obj.title}} {{ obj.errors.title.0}}</p> <p><input type="submit" value="提交"></p> </form> </body> </html>
編輯班級
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>編輯班級</title> </head> <body> <form action="/classes_edit{{nid}}/" method="post"> {% csrf_token %} <p>編輯班級:{{ obj.title }}{{obj.title.errors.0}} </p> <input type="submit" value="提交"> </form> </body> </html>
學生管理1對多
學生顯示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>學生管理</title> </head> <body> <h1>學生管理</h1> <ul> {% for row in student %} <li>{{row.name}} {{row.sex}} <a href="/students_add{{row.id}}/">添加</a> <a href="/students.edit{{row.id}}/">編輯</a> </li> {% endfor %} </ul> </body> </html>
學生添加
<!DOCTYPE html> <html lang="en"> <head> <h1>添加學生</h1> <meta charset="UTF-8"> <title>添加學生</title> </head> <body> <form action="/students_add{{ nid}}/" method="post" novalidate> {% csrf_token%} <p>姓名:{{ obj.name}}{{obj.name.errors.0}}</p> <p>性別:{{ obj.sex}}{{obj.sex.errors.0}} </p> <p>班級:{{ obj.cls_id }}{{ obj.errors.0}} </p> <p><input type="submit" value="提交"></p> </form> </body> </html>
學生編輯
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>老師編輯</title> </head> <body> <form action="/teacher_edit{{ nid }}/" method="post"> {% csrf_token %} <p>老師姓名:{{obj.tname }}</p> <p>班級:{{ obj.classes }}</p> <input type="submit" value="提交"> </form> </body> </html>
老師管理多對多
老師顯示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>老師列表</title> </head> <body> <table border="1"> <thead> <tr><td>ID</td><td>老師</td><td>任教班級</td><td colspan="3">操做</td></tr> </thead> <tbody> {% for teacher in tlist %} <tr> <td>{{ teacher.id }}</td> <td>{{teacher.tname}}</td> <td> {% for row in teacher.c2t.values %} {{ row.title }} {% endfor %} </td> <td><a href="/teacher_add/">添加</a></td> <td><a href="/teacher_edit{{ teacher.id}}/">編輯</a></td> </tr> {% endfor %} </tbody> </table> </body> </html>
老師添加
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加老師</title> </head> <body> <form action="/teacher_add/" method="post" novalidate> {% csrf_token %} <p>老師姓名:{{ obj.tname }}{{obj.tname.errors.0}}</p> <p>任教班級:{{ obj.classes }}{{obj.classes.errors.0}}</p> <p><input type="submit" value="提交"></p> </form> </body> </html>
老師編輯
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>老師編輯</title> </head> <body> <form action="/teacher_edit{{ nid }}/" method="post"> {% csrf_token %} <p>老師姓名:{{obj.tname }}</p> <p>班級:{{ obj.classes }}</p> <input type="submit" value="提交"> </form> </body> </html>
PS:Form驗證組件驗證ajax提交的json
Django的form組件,不只能夠對 瀏覽器端 提交過來的form表單數據作驗證,還能夠對ajax提交的 json數據 作驗證,可是須要在發送以前獲取CSRF-tocken設置在請求頭中;
$.ajax({ type: 'POST', async: false, cache: false, url: '{% url "instance_add" %}', headers:{"X-CSRFToken":$.cookie('csrftoken')}, contentType: "application/json; charset=utf-8", dataType: "json", traditional:true, data:JSON.stringify({ "data_mode_type": $data_mode_type, 'database_type': $database_type, 'host': $host, 'port': $port, 'instance_nikename': $instance_nikename, 'db_business': $db_business, 'DBA': $DBA, 'responsible_person': $responsible_person }), success: function (data) { alert(1) } }); })
from django.shortcuts import render,HttpResponse,redirect from DB_auto.form_validate.add_dbinfo import dbinfo_create import json def instance_add(request): if request.method=='POST': json_data=json.loads(request.body.decode('utf-8')) obj=dbinfo_create(json_data) if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,'add_dbinfo.html')
使用Django開發web程序階段回顧:
1.手動對單表進行增、刪、該、查,手動把ORM操做獲取的數據渲染到模板;(階段1)
2.Form組件(類),自動生成標籤(input、select),並對用戶輸入的數據作規則驗證;(階段2)
3.ModelForm顧名思義就Form和Django的Model數據庫模型結合體,能夠簡單、方便得對數據庫進行增長、編輯操做和驗證標籤的生成;
Form組件和ModelForm的區別
ModelForm是Django Model.py和Form組件的結合體,能夠簡單/快速使用 Form驗證和數據庫操做功能,但不如Form組件靈活,若是在使用Django作web開發過程當中驗證的數據和數據庫字段相關(能夠對錶進行增、刪、改操,注意 Many to many字段,也能夠級聯操做第3張關係表;),建議優先使用ModelForm,用起來更方便些,可是在使用ModelForm的時候慎用fields='__all__',獲取數據庫全部字段勢必形成性能損耗;
使用ModelForm
前端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ form_obj.as_p }} {#<p>姓名:{{form_obj.name }}</p>#} </body> </html>
後端視圖:
from app02 import models from django.forms import ModelForm class UserModalForm(ModelForm): class Meta: model=models.UserInfo #(該字段必須爲 model 數據庫中表) fields= '__all__' #(該字段必須爲 fields 數據庫中表) def add(request): # 實例化models_form if request.method=='GET': obj = UserModalForm() return render(request,'rbac/user_add.html',locals()) else: obj=UserModalForm(request.POST) if obj.is_valid(): data=obj.cleaned_data obj.save() #form驗證經過直接 添加用戶信息到數據庫 return render(request, 'rbac/user_add.html', locals())
model.py
使用使用Form組件和ModelForm注意事項:
一、model.py一點要寫verbose_name='名稱'參數,纔會在前端顯示P標籤的標題;
from django.db import models class Department(models.Model): title=models.CharField(max_length=32,verbose_name='部門名稱') def __str__(self): return self.title class UserInfo(models.Model): name=models.CharField(max_length=32,verbose_name='姓名') emai=models.EmailField(max_length=32,verbose_name='郵箱') pary=models.ForeignKey(Department,verbose_name='部門')
若是使用Form組件在前端顯示標題,能夠設置Form類中的lable參數
class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='郵箱') pary=fields.ChoiceField(choices=[(1,"技術部"),(2,'銷售部'),(3,'市場部'),],label='部門')
二、pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部門'),前端select標籤不能隨數據庫操做實時更新;
在Department表添加一條數據以後,前端select標籤中的數據不能隨數據庫實時更新;
緣由:
不論是ModelForm仍是Form組件本質就是個類,fields(字段)本質就是類中的1個靜態屬性,在類第一次加載時賦值,永遠不會更新;
解決方案1(手動檔):
重寫__init__方法,每次實例化對象,就去獲取一次數據庫內容,從新賦值;
class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='郵箱') pary=fields.ChoiceField(label='部門') # pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部門') def __init__(self,*args,**kwargs): super(UserInfoForm,self).__init__(*args,**kwargs) self.fields['pary'].choices=models.Department.objects.values_list('pk','title')
解決方案2(自動檔)
導入ModelChoiceField 模塊 from django.forms.models import ModelChoiceField
使用ModelChoiceField 字段
from django.forms import fields from django.forms import widgets from django.forms import ModelForm from django.forms.models import ModelChoiceField class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用戶名不能爲空'},label='郵箱') #方案1 pary=ModelChoiceField(queryset=models.Department.objects.all(),label='部門')
自動擋雖好,可是也有缺陷;前端option標籤的內容,須要藉助model.py中的__str__方法,生成;
若是你的web應用涉及到了發表文章、表情評論就必須使用富文本編輯框,小編調研了兩款 Kindeditor、CKeditor;
Kindeditor
1.Kindeditor簡介
http://kindeditor.net/docs/usage.html
2.下載Kindeditor編輯器
http://www.kindsoft.net/down.php
3.引入Kindeditor編輯器
<script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>
4.使用Kindeditor編輯器
a.建立textarea 標籤
<textarea name="content" id="a" cols="30" rows="10"></textarea>
b.初始化Kindeditor
<script> var edit=KindEditor.create('#a',{ width:'700px', height:'500px', uploadJson:'/upload_file/', extraFileUploadParams:{ 'csrfmiddlewaretoken':'{{ csrf_token }}',}}) </script>
c. Kindeditor功能定製
<script> var edit = KindEditor.create('#a', { items: [ //填寫本身想要的功能 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline', 'removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist', 'insertunorderedlist', '|', 'emoticons', 'image', 'link'], width: '700px', height: '500px', uploadJson: '/upload_file/', extraFileUploadParams: { 'csrfmiddlewaretoken': '{{ csrf_token }}', } }) </script>
d.kindeditor的API
// 取得HTML內容 html = editor.html(); // 同步數據後能夠直接取得textarea的value editor.sync(); html = document.getElementById('editor_id').value; // 原生API html = K('#editor_id').val(); // KindEditor Node API html = $('#editor_id').val(); // jQuery // 設置HTML內容 editor.html('HTML內容');
CKeditor結合Django Form
1.下載 django-ckeditor 和pillow
pip install django-ckeditor
pip install pillow
2.setings
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rbac.apps.RbacConfig', 'arya.apps.AryaConfig', 'cmdb.apps.CmdbConfig', 'rest_framework', 'ckeditor', # 'ckeditor_uploader' ]
CKEDITOR_CONFIGS = { 'default': { 'toolbar': ( # ['div','Source','-','Save','NewPage','Preview','-','Templates'], # ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellChecker','Scayt'], # ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'], # ['Form','Checkbox','Radio','TextField','Textarea','Select','Button', 'ImageButton','HiddenField'], # ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'], # ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'], # ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], # ['Link','Unlink','Anchor'], ['Smiley',], # ['Styles','Format','Font','FontSize'], # ['TextColor','BGColor'], # ['Maximize','ShowBlocks','-','About', 'pbckcode'], ), } }
3. 收集Django 項目全部 靜態文件 到的目錄static目錄 / 直接下載ckeditor到項目static
python manage.py collectstatic #設置媒體文件的上傳的路徑 MEDIA_URL="/media/" #MEDIA_ROOT=os.path.join(BASE_DIR,"media") MEDIA_ROOT='/tmp/cmdb_media/' STATIC_ROOT = os.path.join(BASE_DIR, 'static')
注意:此步驟若是提示 須要你設置MEDIA_URL目錄 和STATIC_ROOT 就先暫且設置一下,最終目的是把CKeditor的js插件copy到你的靜態文件存放目錄下;
4.後端生成 textarea +加前端 ckedit 插件生成 富文本編輯
from ckeditor.widgets import CKEditorWidget class comment(Form): #評論框 content =fields.CharField(widget=CKEditorWidget)
{{ obj.content|safe }}
<script src="/static/pligin/ckeditor/ckeditor-init.js"></script>
<script src="/static/pligin/ckeditor/ckeditor/ckeditor.js"></script>
5.CKEditorWidget API
<script> $('.root_reply_btn').click(function () { var $reply_user = $(this).attr('name'); var $pid = $(this).attr('pid'); $('#comment_submit').attr('pid', $pid); CKEDITOR.instances.id_content.insertText('回覆' + $reply_user + ':'); CKEDITOR.instances.id_content.insertHtml('<br>'); CKEDITOR.instances.id_content.focus(); #使CKEditorWidget進入編輯狀態 (MD在後臺測試了半天!!) });
CKEDITOR.instances.id_content.document.$.body.firstChild.textContent 獲取p標籤的文本內容 </script>
6.完成效果
博客連接:http://www.cnblogs.com/wupeiqi/articles/6144178.html
http://www.cnblogs.com/yuanchenqi/articles/7439088.html#3770465
DjangoForm補充:http://www.cnblogs.com/yuanchenqi/articles/7487059.html