django中ModelForm多表單組合的解決方案

django是python語言快速實現web服務的大殺器,其開發效率能夠很是的高!但由於秉承了語言的靈活性,django框架又太靈活,以致於想實現任何功能都有種「條條大路通羅馬」的感受。這麼多種選擇放在一塊兒,如何分出高下?我想此時的場景下就兩個標準:一、相同的功能用最少的代碼實現(代碼少BUG也會少);二、相對最易於理解,從而易於維護和擴展。書歸正傳,web服務容許用戶輸入,基本上要靠表單。而django對錶單的支持力度很是大,咱們用不着在瀏覽器端的html文件裏寫大量<form>代碼,再到web端去匹配form裏的id/name/value、驗證規則,再與持久層數據庫比較並作操做。咱們須要完成的工做很是少,能夠沒有類似的重複代碼。有些複雜的場景,會要求一個表單的內容存放到多張表裏,本文將經過4個部分,闡述它的實現方法。html

 

一、django基礎表單的功能python

定義一個表單很是簡單,繼承類django.forms.Form便可,例如:web

 

 


這個表單類能夠生成HTML形式的form,能夠從request.POST中解析form到ProjectForm類實例。怎麼作到的呢?數據庫

 

看下django.forms.Form定義:django

 

 


註釋說得很清楚,Form這個類就是爲了實現declarative syntax的,也就是說,繼承了Form後,咱們直觀的表達ProjectForm裏要有一個Field名叫name,不關心其語法實現,而經過Form多繼承中的DeclarativeFieldsMetaclass語法糖,將會把name弄到類實例的self.fields裏。瀏覽器

 

咱們重點關注表單的BaseForm類,它實現了基本的邏輯。截選了一小段對接下來的陳述有意義的代碼,作一個簡單的註釋。併發

 

 


因此,基本表單的功能看BaseForm已經足夠了。框架

 

 

二、從模型建立表單post

django對於MVC中的C與M間的映射是很是體貼的,集中體現中Model模型中(好比模型的權限與用戶認證)。那麼,一個模型表明着RDS中的一張表,模型的實例表明着關係數據庫中的一行,而form如何與一行相對應呢?網站

定義一個模型引伸出的表單很是簡單,例如:

 

 


在model中告訴django模型是誰,在fields中告訴django須要在表單中建立哪些字段。django會有一個django.db.models.Field到django.forms.Field的轉換規則,此時會生成Form。咱們看看ModelForm是什麼樣的:

 

 

相似Form類,ModelFormMetaclass就是語法糖,咱們重點看BaseModelForm類:

 

 

 

 

因此,對於ModelForm咱們能夠傳入instance參數初始化表單,能夠調用save()方法直接將從html裏獲得的表單數據持久化到數據庫中。而咱們只須要幾十行代碼就能夠完成這麼多工做。

 

三、通用視圖

django.views.generic.ListView和django.views.generic.edit下的CreateView, UpdateView, DeleteView都是通用視圖。即,咱們又能夠經過它們,把不少重複的工做交給django完成,又能夠少寫不少代碼完成一樣的功能了。這裏僅以CreateView爲例說明,由於它相對最複雜,接下來的多ModelForm的提交也是在CreateView上進行的。

通用視圖使用時,只須要承繼後,再設置model或者form_class便可。好比CreateView就會由django自動的把頁面上POST出的form數據解析到model生成的表單(或者form_calss指定的ModelForm類型表單),同時調用表單的save方法將數據添加到模型對應的數據庫表中。固然GET請求時會生成空form到頁面上。能夠看到,除去定義model或者form類外,幾行代碼就能夠搞定這麼多事。咱們看看CreateView的繼承關係:

簡單介紹下CreateView通用視圖中每一個父類的做用。

 

  1. View是全部視圖類的父類,根據方法名分發請求到具體的get或者post等方法,提供as_view方法。
  2. TemplateResponseMixin提供render_to_response方法將響應經過context上下文在模板上渲染。
  3. ContextMixin在context上下文中加入'view'元素,值爲self實例。
  4. ProcessFormView在GET請求上渲染表單,在POST請求上解析form到表單實例。注意,它會在post請求中判斷表單是否可用,is_valid爲真時,會調用form_valid方法,所以,重寫form_valid方法是第4部分處理多model到一個form的關鍵。
  5. FormMixin容許處理表單,可指定form_class爲某個表單。
  6. SingleObjectMixin生成context上下文,同時根據model模型名稱生成object並添加到上下文中的'object'元素。
  7. ModelFormMixin提供在請求中處理modelform的方式。
  8. SingleObjectTemplateResponseMixin幫助TemplateResponseMixin提供模板。

 

因此,在用CreateView、一個模型、一個模板實現添加一行記錄的功能時是多麼簡單,由於這些父類會自動生成object,渲染到模板,解析form表單,save到數據庫中。因此,從模型建立出的表單ModelForm,配合上通用視圖後,威力巨大!!

 

四、多個ModelForm在一個form裏提交

終於能夠回到本文的主題了。CreateView默認是處理一個Model模型、一個ModelForm表單的,然而,不少時候爲了解耦,會把一張表拆成多張表,經過id關聯在一塊兒。在django的模型中就體現爲ForeignKey、ManyToManyField或者OneToOneField。而在業務邏輯上,須要體現爲一張表單,對應着數據庫裏的多張表。

例如,咱們但願錄入合同,其中合同Model中還有地址Model和項目Model,而項目Model中又有地址Model,等等。

固然,咱們有不少種實現的方案,可是,前面三部分說了那麼多,不是浪費口水的。咱們已經有了通用視圖+ModelForm這樣的利器,難道還須要手動去寫Form表單?咱們已經習慣了在Model裏定義好類型和有點註釋做用還能當label的verbose_name,還須要在forms.Form裏再來一遍?還須要在視圖中寫這麼通用的邏輯代碼嗎?固然不用。

inlineformset_factory是一種方案,但它限制太多,並且有些晦澀,我我的感受是不太好用的。

那麼,從第1部分我介紹的Form裏的prefix,以及第3部分裏類圖中的ProcessFormView容許重定義form_valid,以及第2部分中ModelForm的save方法的行爲控制,解決方案已經一目瞭然了。

拿上面提到的例子來講,咱們建立合同時,指明瞭項目,包括項目地址和合同簽定地址,這涉及到三張表和四條記錄(地址表有兩條)。

咱們三張表的模型以下:

 

 


接着,定義ModelForm表單,這很是簡單:

 

 

 


再寫視圖,這裏要重寫2個方法:

 

 

 


最後寫模板:

 

 

 


至此,咱們能夠只用幾十行代碼就完成複雜的功能,代碼邏輯也清晰可控。

 

從這篇文章裏也能夠看得出,django實在是快速開發網站的必備神器!固然,快速不表明不可以支撐大併發的應用,instagram這個很火的服務就是用django寫的。因爲python和django過於靈活,都將要求django的開發者們惟有更資深才能寫出生產環境下的服務。

相關文章
相關標籤/搜索