在最近的項目開發中,使用了這種數據結構:
主要是管理設備。
我來列舉壹下它的簡化結構,不需要更多,剩下的只是多余或者錦上添花:
設備分類的數據結構(相當於設備的大分類):
[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 =表單.字段
#調試()
用於斷點查看有多少個參數字段,修改是否成功。