django 類視圖可以幫我們輕松處理特定功能的邏輯端朵,django 基本類視圖分為四類:
-
基礎(chǔ)類視圖:
-
展示類視圖:
-
編輯類視圖:
FormView
CreateView
UpdateView
DeleteView
-
日期類視圖:
本文解析編輯類視圖的 FormView。后續(xù)會繼續(xù)對其他類視圖進(jìn)行解析幽告。
實現(xiàn)的功能
FormView 是 Django 為表單處理提供的類視圖宽菜,該類視圖解決以下三種路徑的表單請求:
- 初始GET(空或者預(yù)填充表單)
- 具有無效數(shù)據(jù)的POST(通常重新顯示帶有錯誤的表單)
- 具有有效數(shù)據(jù)的POST(處理數(shù)據(jù)并通常重定向)
其中葵姥,頁面的GET請求將實現(xiàn)初始GET紊浩;用戶提交請求后旭等,根據(jù)用戶提交的數(shù)據(jù)是否通過驗證酌呆,分別實現(xiàn)具有有效數(shù)據(jù)的POST和無效數(shù)據(jù)的POST。
視圖如何實現(xiàn)功能的搔耕?
FormView通過下圖所示邏輯解決上述三中路徑的請求隙袁。
圖中:
1 通過dispatch()方法實現(xiàn);
2 通過get_form() 方法實現(xiàn)弃榨;
3 通過form.is_valid() 方法實現(xiàn)菩收;
4 通過form_valid() 方法實現(xiàn);
6 通過get_form()方法實現(xiàn)鲸睛,
7 通過render_to_response()方法實現(xiàn)娜饵。
它們的組合可以實現(xiàn)FormView的功能:
- 初始GET:通過圖中1-5-6-7路徑實現(xiàn);
- 具有無效數(shù)據(jù)的POST:通過圖中1-2-3-5-7路徑實現(xiàn)官辈;
- 具有有效數(shù)據(jù)的POST:通過圖中1-2-3-4路徑實現(xiàn)箱舞。
從圖中我們可以看到,2钧萍、6都是通過get_form() 方法實現(xiàn)form的實例化褐缠,它們的區(qū)別在于:
- 2中的form實例化對應(yīng)的是POST請求,它將生成含有初始數(shù)據(jù)风瘦、POST方法中獲取到的數(shù)據(jù)的表單實例(如果初始數(shù)據(jù)與POST方法中數(shù)據(jù)的關(guān)鍵詞相同队魏,表單綁定的是POST中的數(shù)據(jù));
- 6中的form實例化對應(yīng)的是GET請求,它將生成具備初始數(shù)據(jù)的表單實例胡桨;
實現(xiàn)上述流程官帘,F(xiàn)ormView需要設(shè)置以下兩個屬性(方法):
- form_class : 表單類,用于 get_form() 方法昧谊,這里使用 ApplicationForm 刽虹;該屬性也可以通過重寫 get_form_class() 方法進(jìn)行定義;
- success_url:表單驗證成功后跳轉(zhuǎn)到的url呢诬,用于 form_valid() 方法涌哲。該屬性也可以通過重寫 get_success_url() 方法進(jìn)行定義。
如果需要設(shè)置表單的初始值尚镰,可以設(shè)置一下屬性:
- initial:字典阀圾,key為表單字段名,用于get_form()方法狗唉,該屬性也可以通過重寫get_initial() 方法進(jìn)行定義初烘。
代碼是如何實現(xiàn)上述功能的
GET請求
get() 方法來處理 GET請求,F(xiàn)ormView內(nèi)置的get() 方法為:
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates a blank version of the form.
"""
return self.render_to_response(self.get_context_data())
該方法與實現(xiàn)了圖1中5-6-7路徑分俯,其中肾筐,5-6通過self.get_context_data()實現(xiàn),7通過self.render_to_response()實現(xiàn)缸剪。
get_contecxt_data() 方法具體內(nèi)容為:
def get_context_data(self, **kwargs):
"""
Insert the form into the context dict.
"""
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
return super(FormMixin, self).get_context_data(**kwargs)
如果 get_context_data() 的參數(shù)中沒有 form 吗铐,這里將使用 form_class 設(shè)置的表單類創(chuàng)建 form實例作為關(guān)鍵詞參數(shù) ‘form’ 的值,django 內(nèi)部會將所有的關(guān)鍵詞參數(shù) 傳入響應(yīng)的 context 中橄登,因此抓歼,我們可以在 HTML 中通過 {{ form }}來訪問表單實例讥此。
POST請求
post() 方法來處理 POST請求拢锹,F(xiàn)ormView內(nèi)置的post() 方法為:
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance with the passed
POST variables and then checked for validity.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
該方法實現(xiàn)了圖中1-2-3-5-7路徑和1-2-3-4路徑。其中:
? 2 通過form = self.get_form()實現(xiàn)萄喳;
? 3 通過form.is_valid()實現(xiàn)卒稳;
? 5-7 通過 self.form_invalid(form) 實現(xiàn);
? 4 通過self.form_valid(form)實現(xiàn)他巨。
self.form_invalid(form)
處理沒有通過驗證的表單充坑。它的代碼是:
def form_invalid(self, form):
"""
If the form is invalid, re-render the context data with the
data-filled form and errors.
"""
return self.render_to_response(self.get_context_data(form=form))
它與 GET請求中 get() 方法實現(xiàn)的功能類似,唯一的區(qū)別在于:它將未通過驗證的表單實例 傳入self.get_context_data() 方法中染突,這樣 context中的form為驗證未通過的表單實例捻爷,而不是 get() 中的空的表單實例。
self.form_valid(form)
處理通過驗證的表單份企,它的代碼是:
def form_valid(self, form):
"""
If the form is valid, redirect to the supplied URL.
"""
return HttpResponseRedirect(self.get_success_url())
它只實現(xiàn)了一項功能:跳轉(zhuǎn)到成功頁面也榄。
這里采用的是HttpResponseRedirect類進(jìn)行處理,HttpResponseRedirect 實現(xiàn) HTTP 302。簡單來說甜紫,它將向定義的跳轉(zhuǎn)成功頁面 發(fā)起 GET請求降宅。
HTTP 302
RFC1945,也就是HTTP1.0在介紹302時說囚霸,如果客戶端發(fā)出POST請求后腰根,收到服務(wù)端的302狀態(tài)碼,那么不能自動的向新的URI發(fā)送重復(fù)請求拓型,必須跟用戶確認(rèn)是否該重發(fā)额嘿,因為第二次POST時,環(huán)境可能已經(jīng)發(fā)生變化(嗯劣挫,POST方法不是冪等的)岩睁,POST操作會不符合用戶預(yù)期。但是揣云,很多瀏覽器(user agent我描述為瀏覽器以方便介紹)在這種情況下都會把POST請求變?yōu)镚ET請求捕儒。
? RFC2616,也就是HTTP1.1在介紹302時說邓夕,如果客戶端發(fā)出非GET刘莹、HEAD請求后,收到服務(wù)端的302狀態(tài)碼焚刚,那么就不能自動的向新URI發(fā)送重復(fù)請求点弯,除非得到用戶的確認(rèn)。但是矿咕,很多瀏覽器都把302當(dāng)作303處理了(注意岸啡,303是HTTP1.1才加進(jìn)來的术陶,其實從HTTP1.0進(jìn)化到HTTP1.1,瀏覽器什么都沒動),它們獲取到HTTP響應(yīng)報文頭部的Location字段信息境氢,并發(fā)起一個GET請求社裆。