當前位置:成語大全網 - 書法字典 - 如何使用django動態定義表單?

如何使用django動態定義表單?

第壹節數據結構

在最近的項目開發中,使用了這種數據結構:

主要是管理設備。

我來列舉壹下它的簡化結構,不需要更多,剩下的只是多余或者錦上添花:

設備分類的數據結構(相當於設備的大分類):

[python]查看純文本

類別類別(型號。型號):

名稱=型號。CharField(max_length=40)

特定設備的數據結構:

[python]查看純文本

類別設備(型號。型號):

名稱=型號。CharField(max_length=100)

類別=型號。ForeignKey(類別,related_name="cg_equip_list ")

設備參數的數據結構:

[python]查看純文本

類別特征(型號。型號):

類別=型號。ForeignKey(Category,related _ name = " eq _ characteristics ")

名稱=型號。CharField(max_length=40)

設備參數值的數據結構:

[python]查看純文本

類別特性值(模型。型號):

設備=型號。ForeignKey(Equipment,related _ name = " eq _ character istic _ values ")

特性=模型。ForeignKey(Characteristic,related_name="eq_key_values ")

價值=型號。CharField(max_length=100)

很多人可能會問:為什麽不直接把設備的參數值定義到設備的數據結構中呢?

我的回答是:妳可以自己定義更多的設備。

每個設備的參數都不壹樣。例如,計算機的參數基本上是:

CPU頻率,內存大小,硬盤大小,顯示器類型,顯示器大小等等。

打印機參數可以是這樣的:

最大打印紙格式、打印機類型等。

兩個參數不同的設備如何在數據庫中保存數據?

根據上面的問題,我們將需要建立兩個表,壹個計算機表和壹個打印機表,它們的列中分別包含相應的參數。在這種情況下,如果多了壹個設備,比如攝像頭,我們就必須建立另壹個數據表,並且厭倦了維護數據庫。

但根據參數與設備的分離,各類設備的參數都以參數表的形式定義,並通過外鍵鏈接到設備分類表。

然後把具體設備的各種參數的值放到參數值表中,就可以保存每臺設備的參數了。

在參數值數據表中,壹個參數屬於哪個設備主要是根據設備的id和參數的id來確定的。

這種表結構比較通用。

比較麻煩的是,增加壹個設備時,要同時增加其對應的參數值,而其參數不在設備表中,需要通過參數表查詢:

參數表中,參數的設備分類id =具體設備的設備分類ID的那壹行就是該設備所擁有的參數。

例如:

設備分類表中的數據:

id名稱

1臺式電腦

2臺打印機

參數在參數表中保存以下幾行數據:

id名稱類別

1 CPU頻率1

2 MAC地址1

3內存大小1

4顯示尺寸1

5顯示類型1

6硬盤大小1

7最大打印格式2

壹眼就能看出,臺式電腦有6個參數,打印機有1個參數。

第二節解決方案的選擇

數據結構很清楚,現在的問題是如何輸入這些參數的值?

難點在於每個設備的參數個數是不確定的,所以在定義壹個表單的時候,我們不能固定表單的字段個數,只能動態的增減字段個數。

那麽,如何動態生成具有不同字段數量的表單呢?

在這裏,我的想法是,既然參數個數存儲在數據庫中,那就必須通過查詢數據庫來動態生成。

那麽,使用django應該有哪些具體的方法呢?

我自己嘗試了很多方法,在調試過程中慢慢找到了解決這個問題的辦法。

過程是這樣的:

我開始考慮的解決方案是:

1.使用ajax動態生成表單。

2.在添加設備時,使用admin和inline中的方法添加幾個參數。

3.使用formset來制作它

4.使用formtools中的formwizard來制作。

問題的困境在於參數值的表中有兩個外鍵,不容易確定該怎麽做。

我只是嘗試了前三種方法,調試了壹兩天就放棄了。主要原因是:

1.ajax目前對我來說還是比較陌生的。要想用好,就得轉而學習壹些相關的javascript庫,壹時半會兒效果不好。

2.admin中的inline是針對壹個數據模型的,添加的參數值是壹樣的,比如可能會增加到6個CPU參數,在Admin中使用,挖出來自己用還是挺麻煩的。

3.FormSet中的form也需要同樣的數據模型,但是妳根本不能用FormSet,因為參數值表中的兩個外鍵都很高。FormSet用於在數據結構簡單的情況下輸入數據。

formset有壹個函數叫add_fields,很有用,但是表單沒有這個函數。原理很簡單理解,就是form ['field _ name'] = forms。charfield()等等,並且您可以向表單實例添加壹個字段。

我認為formset可以解決問題,但前提是在構造formset之前,需要事先知道formset中有多少個帶參數值的表單(也就是要知道某個分類設備有多少個參數,在formset中構造幾個帶特征值的表單就好,每個表單用不同的參數初始化初始)。

在壹個頁面中,先添加設備的表單,因為設備的參數個數不固定,所以需要動態添加壹個包含幾個參數值表單的表單集,這樣就可以選擇設備的參數。formset更適合使用固定數量的表單,所以使用formset比較困難。如果要使用這種方法,將使用ajax根據用戶選擇的設備類型動態生成formset。

窗體集需要在視圖中初始化。

所以我沒有用這個方法。

我使用表單向導。

Django的contrib提供了formtools,用了之後真的很好用。我是通過看英文文檔壹點壹點學會使用的,感覺真的很方便。

然而,上述4種解決方案的實施過程非常困難。我花了3天才得到壹些線索。

第三節具體實施

django中的表單向導使用起來非常簡單。

[python]查看純文本

從django.utils.translation導入ugettext_lazy as _

從django進口表格

從django.forms.formsets導入BaseFormSet

從django.forms.fields導入文件字段

從django.forms.util導入驗證錯誤

快捷方式導入render_to_response

從django . contrib . form tools . wizard導入表單向導

從DDT CMS . office . Equipment . modes導入設備,特性,特性值

設備類別表格(表格。模型表單):

類別元:

型號=設備

類特征值形式(形式。表單):

def clean(自我):

a =自身字段

s=self.data

self.cleaned_data = {}

#以下段落是從django的forms.py中的full_clean復制而來的

對於名稱,self.fields.items()中的字段:

# value_from_datadict()從數據字典中獲取數據。

#每種小部件類型都知道如何檢索自己的數據,因為有些

# widgets將數據分割到幾個HTML字段中。

value = field . widget . value _ from _ datadict(self . data,self.files,self.add_prefix(name))

嘗試:

如果是instance(field,FileField):

initial = self.initial.get(name,field.initial)

value = field.clean(值,初始值)

否則:

value = field.clean(值)

self.cleaned_data[name] = value

if hasattr(self,' clean_%s' % name):

value = getattr(self,' clean_%s' % name)()

self.cleaned_data[name] = value

除了驗證錯誤,e:

自我。_ errors[name]= self . error _ class(e . messages)

如果名稱在self.cleaned_data中:

del self.cleaned_data[name]

#cl=self.cleaned_data

# debug()& lt;& lt& lt調試時,檢查cl的值,主要是self.cleaned_data的值。如果它被歸還,它將會丟失。

返回self.cleaned_data

class EquipmentCreateWizard(表單向導):

定義完成(自身、請求、表單_列表):

返回render _ to _ response(' equipment/done . html ',

{

form _ data ':[form _ list中表單的form.cleaned _ data,

})

def get_form(self,step,data=None):

"返回給定步驟的表單實例的幫助器方法."

form = self.form_list[step](data,prefix = self . prefix _ for _ step(step),initial=self.initial.get(step,None))

如果步長== 1:

如果數據:

CG = data . get(' 0-類別',1)

cs = Characteristic.objects.all()。過濾器(類別__id=cg)

對於cs中的c:

form . fields[' character istic-'+str(c . id)]=表單。CharField(label = c.name)

g =表單.字段

#調試()

退貨單

#從wizard.py中復制它以進行更改。

def render(自身、表單、請求、步驟、上下文=無):

"呈現給定的表單對象,返回壹個HttpResponse . "

舊數據=請求。郵政

prev_fields = []

如果是舊數據:

隱藏=表單。HiddenInput()

#收集前面步驟中的所有數據,並將其呈現為HTML隱藏字段。

對於範圍內的I(步長):

old_form = self.get_form(i,old_data)

hash_name = 'hash_%s' % i

prev _ fields . extend([BF . as _ hidden()for BF in old _ form])

prev _ fields . append(hidden . render(hash _ name,old_data.get(hash_name,self.security_hash(request,old_form)))

如果步長== 1:

CG = old _ data . get(' 0-類別',1)

cs = Characteristic.objects.all()。過濾器(類別__id=cg)

對於cs中的c:

form . fields[' character istic-'+str(c . id)]=表單。CharField(label = c.name)

g =表單.字段

#調試()

如果步長== 2:

調試()

return super(equipment create wizard,self)。呈現(表單、請求、步驟、上下文=無)

def get_template(self,step):

返回“設備/向導_%s.html”步驟

EquipmentCreateWizard也可以放在views.py,我覺得更合理。

在EquipmentCreateWizard中,我試圖修改process_step函數,但無法得到正確的結果。後來我修改了get_form,都是想從django的formtools的wizard.py復制然後修改。

get_form的修改也沒有得到正確的結果。後來修改了render函數,第二步我展示了動態參數的個數。但是在done鏈接的末尾,第二個表單有formdata,並且是壹個空的{}。

於是我又修改了get_form函數,無非是判斷是不是第二步,然後動態的給第二個表單添加幾個字段:

[python]查看純文本

如果步長== 1:

CG = old _ data . get(' 0-類別',1)

cs = Characteristic.objects.all()。過濾器(類別__id=cg)

對於cs中的c:

form . fields[' character istic-'+str(c . id)]=表單。CharField(label = c.name)

g =表單.字段

#調試()

這個代碼在get_form和render中都有,就是判斷是否是第二步,然後根據第1步選擇的設備的分類查詢具體的分類,然後根據分類獲取分類設備的參數,然後根據參數的個數修改表單的參數字段的個數。

Characteristic-'+str(c.id)用於將來保存數據時拆分字符串,得到參數的id,保存characteristic-1,characteristic-2的值...在參數值表中。

g =表單.字段

#調試()

用於斷點查看有多少個參數字段,修改是否成功。