通常會建立forms.py文件,單獨存放form模塊。html
Form 專門作數據驗證,並且很是強大。有如下兩個插件:前端
urls.pymysql
url(r'^index/$', views.index),
views.pyjquery
def index(request): from cmdb.forms import UserInfoForm obj = UserInfoForm() return render(request, 'index.html', {'obj':obj})
forms.pyajax
from django import forms from django.forms import widgets, fields from cmdb import models class UserInfoForm(forms.Form): user = fields.CharField( required=False, widget=widgets.Textarea(attrs={'class':'c1'}) ) pwd = fields.CharField( max_length=12, widget=widgets.PasswordInput(attrs={'class':'c1'}) ) user_type = fields.ChoiceField( # choices=[(1,'普通用戶'),(2,'超級用戶')], # 手動填寫 choices=models.UserType.objects.values_list('id','name'), # 數據庫中獲取 widget=widgets.Select )
models.py正則表達式
class UserType(models.Model): name = models.CharField(max_length=32)
index.htmlsql
<body> <p>{{ obj.user }}</p> <p>{{ obj.pwd }}</p> <p>{{ obj.user_type }}</p> <p>{{ obj.user_type2 }}</p> </body>
forms裏面,定義的類,類裏的user、pwd、user_type都是靜態字段(類變量),這些都是屬於類的。數據庫
在__init__
裏面寫的屬於對象。django
而啓動Django的時候,類變量一次性都加在到內存了。json
上面views.py裏的obj = UserInfoForm()
生成了一個對象,會執行類裏的__init__
方法,會把類裏的字段封裝到obj.fields裏面(裏面有user、pwd、user_type三個字段),因此:
views.py
def index(request): from cmdb.forms import UserInfoForm from cmdb import models obj = UserInfoForm() obj.fields['user_type'].choices = models.UserType.objects.values_list('id','name') return render(request, 'index.html', {'obj':obj})
這樣,數據庫添加數據,網頁上就能夠實時更新了。可是若是有不少choices的話,也會寫不少,也很差。
因此不在views.py裏作修改,能夠在forms.py裏類的__init__
構造方法裏寫
forms.py
from django import forms from django.forms import widgets, fields from cmdb import models class UserInfoForm(forms.Form): user = fields.CharField( required=False, widget=widgets.Textarea(attrs={'class':'c1'}) ) pwd = fields.CharField( max_length=12, widget=widgets.PasswordInput(attrs={'class':'c1'}) ) user_type = fields.ChoiceField( # choices=[(1,'普通用戶'),(2,'超級用戶')], # 手動填寫 choices=[], # 構造方法裏取,這裏只定義就能夠了,不須要在獲取一次。 widget=widgets.Select ) #### 另一種寫法 方式 #### user_type2 = fields.ChoiceField( widget=widgets.Select(choices=[]) ) def __init__(self, *args, **kwargs): super(UserInfoForm,self).__init__(*args, **kwargs) self.fields['user_type'].choices = models.UserType.objects.values_list('id','name') #### 另一種寫法 方式 #### self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')
上面1.1裏是本身獲取實現的,Django也能夠自動實現,只是實現的不太好。
django 裏自動實現的,看user_type3
from django import forms from django.forms import widgets, fields from django.forms.models import ModelChoiceField from cmdb import models class UserInfoForm(forms.Form): user = fields.CharField( required=False, widget=widgets.Textarea(attrs={'class':'c1'}) ) pwd = fields.CharField( max_length=12, widget=widgets.PasswordInput(attrs={'class':'c1'}) ) user_type = fields.ChoiceField( choices=[], widget=widgets.Select ) user_type2 = fields.ChoiceField( widget=widgets.Select(choices=[]) ) user_type3 = ModelChoiceField( queryset=models.UserType.objects.all() ) def __init__(self, *args, **kwargs): super(UserInfoForm,self).__init__(*args, **kwargs) self.fields['user_type'].choices = models.UserType.objects.values_list('id','name') self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')
使用Django自動提供的這個方法,有一點很差的就是:須要本身在models里加上__str__
方法
class UserType(models.Model): name = models.CharField(max_length=32) def __str__(self): return self.name
ModelChoiceField(ChoiceField) ... django.forms.models.ModelChoiceField queryset, # 查詢數據庫中的數據 empty_label="---------", # 默認空顯示內容 to_field_name=None, # HTML中value的值對應的字段(html源碼中value不一樣) limit_choices_to=None # ModelForm中對queryset二次篩選
ModelChoiceField 是單選的,多選的是:ModelMultipleChoiceField
加一個字典,生成顯示默認值
views.py
def index(request): from cmdb.forms import UserInfoForm obj = UserInfoForm({'user':'lgeng','user_type':'2'}) # 默認值 return render(request, 'index.html', {'obj':obj})
views.py
def index(request): from cmdb.forms import UserInfoForm if request.method == 'GET': obj = UserInfoForm({'user':'lgeng','user_type':'2'}) return render(request, 'index.html', {'obj':obj}) elif request.method == 'POST': obj = UserInfoForm(request.POST, request.FILES) # request.FILES:接收文件 obj.is_valid() # 數據驗證
正則表達式驗證成功了,可是若是用戶名重複了,也是不讓執行的,如何自定製呢?
forms.py
from django.core.exceptions import ValidationError class RegisterForm(forms.Form): user = fields.CharField email = fields.EmailField def clean_user(self): # 對user作一個單獨的驗證 c = models.UserType.objects.filter(name=self.cleaned_data['user']) if c : # 若是用戶名已經存在,提示報錯信息 raise ValidationError('用戶名已經存在',code='xxx') else: # 不存在,驗證經過 return self.cleaned_data['user'] def clean_email(self): # 對email作單獨的驗證 # 驗證必需要有返回值 return self.cleaned_data['email'] # 驗證順序: # 先user字段,在clean_user方法 # 再email字段,在clean_email方法
forms.py
class LoginForm(forms.Form): user = fields.CharField # 這裏也能夠validators=[],自定義正則數據驗證 email = fields.EmailField(validators=[]) def clean_user(self): pass def clean_pwd(self): pass # 當用戶名和密碼驗證總體錯誤,報錯給下面的方法 def clean(self): c = models.User.objects.filter(name=self.cleaned_data.get('user'),pwd=self.cleaned_data.get('pwd')) if c: return self.cleaned_data else: raise ValidationError("用戶名或密碼錯誤") # 自定義一些其餘驗證操做 def _post_clean(self): pass
這麼多鉤子,這麼強大的數據驗證,驗證順序是:
form循環,
_post_clean
鉤子怎麼去記:經過看源碼去找:
先找is_valid
,找到errors
,找到full_clean
,全部的鉤子基於full_clean
執行的
views.py(僞代碼)
def register(request): from cmdb.forms import RegisterForm from django.core.exceptions import NON_FIELD_ERRORS obj = RegisterForm(request.POST) if obj.is_valid(): obj.cleand_data else: obj.errors { 'user':[{'code':'required','message':'xxxx'}], 'pwd':[{'code':'required','message':'xxxx'}], # 上面每一個字段的錯誤信息放在裏面,那clean總的錯誤信息放在哪裏? '__all__':[], # 特殊的總體錯誤信息,放在這裏 # NON_FIELD_ERRORS:[], 和 __all__ 一個意思。 }
無論提交數據是瀏覽器提交、仍是curl方式,仍是ajax提交,只要是request.POST
均可以作驗證。
urls.py
url(r'^login.html$', views.login),
views.py
from django import forms from django.forms import widgets, fields class LoginForm(forms.Form): username = fields.CharField() password = fields.CharField( max_length=64, min_length=12 ) def login(request): import json res = {'status':True, 'error':None, 'data': None} if request.method == "GET": return render(request,"login.html") elif request.method == "POST": obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleand_data) else: # print(obj.errors, type(obj.errors)) res['error'] = obj.errors.as_json() # 轉爲json格式 return HttpResponse(json.dumps(res))
login.html
<body> <form id="fm"> {% csrf_token %} <p><input type="text" name="username" /></p> <p><input type="password" name="password" /></p> <a id="submit">ajax提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script> // 頁面框架加載完自動執行 $('#submit').click(function(){ $.ajax({ url:'/login.html', type:'POST', data:$('#fm').serialize(), success:function(arg){ console.log(arg); arg = JSON.parse(arg); // 轉爲字典 console.log(arg); }, error: function(){ } }) }) </script> </body>
上面的方式能夠實現,只是前端須要反解兩次,不太好,下面優化一下。
views.py
from django import forms from django.forms import widgets, fields class LoginForm(forms.Form): username = fields.CharField() password = fields.CharField( max_length=64, min_length=12 ) # 序列化,轉換爲指定數據類型 import json from django.core.exceptions import ValidationError class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, ValidationError): return {'code':field.code, 'messages': field.messages} else: return json.JSONEncoder.default(self, field) def login(request): res = {'status':True, 'error':None, 'data': None} if request.method == "GET": return render(request,"login.html") elif request.method == "POST": obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleand_data) else: # print(obj.errors, type(obj.errors)) # res['error'] = obj.errors.as_json() # 轉爲json格式 print(type(obj.errors.as_data())) for k,v in obj.errors.as_data().items(): print(k,v) # 這裏v是ValidationError類型,不能序列化 res['error'] = obj.errors.as_data() result = json.dumps(res, cls=JsonCustomEncoder) return HttpResponse(json.dumps(result))
Json.dumps
用來作序列化,可是隻能序列化一些簡單的基本數據類型。對於沒法序列化的數據類型,只能自定製了。
因爲json.dumps時沒法處理datetime日期,因此能夠經過自定義處理器來作擴展,如:
import json from datetime import date from datetime import datetime class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): return o.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, date): return o.strftime('%Y-%m-%d') # 轉成字符串類型 else: return json.JSONEncoder.default(self, field) v = {'k':'123', "k1":datetime.datetime.now()} ds = json.dumps(v, cls=JsonCustomEncoder)
上面本身寫實現也能夠,不過Django提供了方法作序列化
from django.core import serializers v = models.tb.objects.all() data = serializers.serialize("json", v)
import json from datetime import date from datetime import datetime 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) v = models.tb.objects.values('id','name','ctime') v = list(v) # 把(相似列表的queryset類型)轉換爲列表 v = json.dumps(v,cls=JsonCustomEncoder) # 這裏cls只對ctime操做。 # 若是沒有ctime這個類型,只寫上邊的三句就能夠實現
注:form裏__all__
總體的錯誤信息,前端展現以下:{{ obj.non_field_errors }}
轉載請務必保留此出處:http://www.cnblogs.com/lgeng/articles/7366370.html
<!-- END -->
《版本說明》: 本文轉自 -- http://blog.csdn.net/fgf00/article/details/54629502