原文地址:html
關於在同1個頁面多個表單提交的問題,其實是項目中遇到的1個小問題。關於這個問題,主要有2個須要解決的問題:django
多個表單的渲染問題app
多個表單提交時外鍵的處理問題post
下面咱們分別進行說明。
當時在建模的時候使用了相似以下的方式:spa
from django.db import models class Store(models.Model): name = models.CharField('名稱', max_length=20) first = models.FloatField('首重') additional = models.FloatField('次重') img = models.ImageField('圖片', upload_to='store/1') class Depot(models.Model): s_name = models.ForeignKey(Store, verbose_name='倉庫') src = models.CharField('始發地', max_length=20) dest = models.CharField('目的地', max_length=20) days = models.PositiveSmallIntegerField('須要的天數') class Address(models.Model): s_name = models.ForeignKey(Store, verbose_name='倉庫') country = models.CharField('國家', max_length=20) state = models.CharField('省份', max_length=10) city = models.CharField('城市', max_length=10) description = models.TextField('描述', blank=True)
在這裏,1個倉庫的數據主要由3個表組成,分別爲它的一些基礎信息,能夠配送的範圍、天數及其餘一些附加信息組成。而後其頁面以下所示:插件
而公司的需求就是咱們要在商戶端上讓客戶在建立倉庫時填寫上述的內容,因爲我比較懶,而公司給出的時間也不是很充裕,因而直接使用ModelForm來實現,而不須要一一的建立表單了。換句話說,咱們要將多個模型表在同1個頁面中渲染出來,對於這樣的問題,主要有4種解決的方案:code
在1個form組件中使用多個模型表單類orm
使用django提供的modelform_factory來解決csrf
使用第3方插件django-betterforms或django-multipleformwizard這樣的插件
使用元類,而後繼承BaseForm進行表單的重寫。
這裏咱們使用第1種解決方案來實現多個表單渲染的問題。
這裏咱們在forms模塊下新建3個模型表單類:
from django.forms import ModelForm from models import Store, Address, Depot class StoreForm(ModelForm): class Meta: model = Store fields = '__all__' class AddressForm(ModelForm): class Meta: model = Address exclude = ['s_name'] class DepotForm(ModelForm): class Meta: model = Depot exclude = ['s_name']
而後在視圖中引入這3個表單:
from django.shortcuts import render_to_response, HttpResponseRedirect from django.template import RequestContext from forms import StoreForm, AddressForm, DepotForm def store_add(req): if req.method == 'POST': ... else: sf = StoreForm() af = AddressForm() df = DepotForm() return render_to_response('store_add.html', { 'sf': sf, 'af': af, 'df': df, }, context_instance=RequestContext(req))
默認狀況下,咱們先將對應的表單渲染出來先。在這裏咱們往模板中輸出了多個變量,而後在模板中手動進行以下的處理:
<form action="" method='post' enctype='multipart/form-data'> {% csrf_token %} {{ sf.as_p }} {{ df.as_p }} {{ af.as_p }} <input type="submit" value = "添加" /> </form>
在這裏,咱們在1個表單中輸出多個表單,其頁面以下所示:
能夠看到其效果與後臺的頁面相差不是很大,只是沒有對應的樣式而已。
接着咱們須要處理多個表單提交時的處理問題。
def store_add(req): if req.method == 'POST': sf = StoreForm(req.POST, req.FILES) af = AddressForm(req.POST) df = DepotForm(req.POST) if sf.is_valid() and af.is_valid() and df.is_valid(): sf.save() df.save() af.save() return HttpResponseRedirect('store') ...
在這裏咱們直接對這3個表單進行保存,結果出現了這樣1個錯誤。
NOT NULL constraint failed: app_depot.s_name_id
因爲咱們使用了1個外鍵進行了約束,而使用上述的方式會致使數據表中的s_name_id
的字段數值爲NULL,從而致使了錯誤。而上述的方式時直接就提交給數據庫了,致使後面的外鍵沒法被知足。爲了解決這個問題,咱們採用延遲提交給數據庫的方式:
def store_add(req): if req.method == 'POST': ... if sf.is_valid() and af.is_valid() and df.is_valid(): form = sf.save(commit=False) sf.save() dform = df.save(commit=False) dform.s_name = form dform.save() aform = af.save(commit=False) aform.s_name = form aform.save() return HttpResponseRedirect('store') else: ...
在這裏,咱們先讓第1張表先不提交,將其保存爲1個變量form中。而第2個張表也先不提交,咱們將其實例的s_name修改成以前的第1張表返回的結果,而後再進行保存。這樣咱們就實現了多張表的依賴致使的問題了。最後咱們使用重定向的方式將成功添加後的頁面跳轉到該商戶的倉庫列表中。
其跳轉後的頁面以下所示:
這樣咱們就解決了在1個頁面提交多個表單的問題。
實際關於Django在1個頁面提交多個表單的問題,實際上問題不是不少,只要解決了渲染和提交時處理的問題,實際這個問題就迎刃而解了。重要的是如何拆分問題和解決問題的思路。
參考文章: