2009-08-13 55 views
0

我正在用django構建我的第一個表單,而且我看到了一些我根本沒有想到的行爲。使用django的Form類是否保持狀態?

class AssignmentFilterForm(forms.Form): 
filters = [] 
filter = forms.ChoiceField() 

def __init__(self, *args, **kwargs): 
    super(forms.Form, self).__init__(*args, **kwargs) 
    self.filters.append(PatientFilter('All')) 
    self.filters.append(PatientFilter('Assigned', 'service__isnull', False)) 
    self.filters.append(PatientFilter('Unassigned', 'service__isnull', True)) 

    for i, f in enumerate(self.filters): 
     self.fields["filter"].choices.append((i, f.name)) 

當我輸出此表單模板:

{{ form.as_p }} 

我看到正確的選擇,我定義了一個表單類。但是,刷新頁面後,我在選擇框中看到列表三次。再次點擊刷新會在列表中顯示10次!

這是我的觀點:

@login_required 
def assign_test(request): 
pg = PhysicianGroup.objects.get(pk=physician_group) 

if request.method == 'POST': 
    form = AssignmentFilterForm(request.POST) 
    if form.is_valid(): 
     yes = False 
else: 
    form = AssignmentFilterForm() 
    patients = pg.allPatients().order_by('bed__room__unit', 'bed__room__order', 'bed__order') 

return render_to_response('hospitalists/assign_test.html', RequestContext(request, {'patients': patients, 'form': form,})) 

我在做什麼錯?

謝謝,皮特

回答

1

我拿起Pro Django這本書來回答這個問題。這是一本很棒的書,我強烈推薦!

解決的辦法是使雙方的選擇現場和幫助我的兩個變種的實例變量:

class AssignmentFilterForm(forms.Form): 
def __init__(self, pg, request = None): 
    super(forms.Form, self).__init__(request) 
    self.filters = [] 

    self.filters.append(PatientFilter('All')) 
    self.filters.append(PatientFilter('Assigned', 'service__isnull', False)) 
    self.filters.append(PatientFilter('Unassigned', 'service__isnull', True)) 
    self.addPhysicians(pg) 

    self.fields['filter'] = forms.ChoiceField() 
    for i, f in enumerate(self.filters): 
     self.fields['filter'].choices.append((i, f.name)) 

清理出的選擇工作,但肯定會造成線程問題。

1

要追加到每一個類變量self.filters。相反,通過在__init__開始時執行self.filters = []將其變爲PER-INSTANCE變量。

+0

哈哈,謝謝。新的語言。同樣的問題發生,因爲過濾器也是靜態的。我如何解決它? – slypete 2009-08-13 22:52:09

+0

在'__init__'循環之前,先執行'self.fields ['filter']。choices = []'來清理你之前從「上一輪」得到的內容。可能不是固有的線程安全,所以如果你是多線程的話,你需要謹慎,但線程對於語言初學者來說是一個雷區,所以如果你能避免它,你會變得更加快樂。 – 2009-08-13 23:00:04

+0

大聲笑,我不是初學者,只是爲了這種語言。我如何使過濾器實例var?我現在很困惑爲什麼Django的表單文檔使用靜態變量。 – slypete 2009-08-13 23:07:46

7

這實際上是Python的一項功能,它吸引了很多人。

當您在該類上定義變量時,與filters = []一樣,表達式的右半部分在初始定義類時進行評估。所以當你的代碼第一次運行時,它會在內存中創建一個新列表並返回對這個列表的引用。因此,每個AssignmentFilterForm實例都會有自己的過濾器變量,但它們都將指向內存中的同一個列表。要解決這個問題,只需將self.filters的初始化設置爲您的__init__方法。

大多數情況下,您不會遇到此問題,因爲您使用的類型未存儲爲參考。數字,布爾值等被存儲爲它們的值。字符串通過引用存儲,但字符串是不可變的,這意味着每次更改新的字符串並返回一個新的引用時,都必須在內存中創建一個新的字符串。

指針不會經常以腳本語言呈現,所以當它們這樣做時,它通常會讓人感到困惑。

這裏有一個簡單的空閒會話例子來說明發生了什麼

>>> class Test(): 
    myList = [] 
    def __init__(self): 
     self.myList.append("a") 


>>> Test.myList 
[] 
>>> test1 = Test() 
>>> Test.myList 
['a'] 
>>> test1.myList 
['a'] 
>>> test2 = Test() 
>>> test2.myList 
['a', 'a'] 
>>> test1.myList 
['a', 'a'] 
>>> Test.myList 
['a', 'a'] 
+0

謝謝你的解釋。那麼爲什麼django會選擇每個類別的變量,而我該如何改變呢? – slypete 2009-08-13 23:17:29

+0

在文檔中是否有您正在查看的特定頁面?在所有的表單例子中,我看到他們從不使用'__init__'。我相信在這種情況下,您可能實際上想要消除'__init__',並將其作爲類定義的一部分進行初始化。 – spbogie 2009-08-13 23:38:11

+0

我認爲數字,布爾等的引用也被存儲,但是就像字符串一樣,數字在Python中也是不可變的。 – mipadi 2009-08-16 15:31:30

0

如上回答,您需要初始化過濾器作爲一個實例變量:

def __init__(...): 
    self.filters = [] 
    self.filters.append(...) 
    # ... 

如果您想了解更多關於如何Form類可以工作,您應該在Django wiki中閱讀以下頁面:

它討論了Model類的內部,但你會發現字段的一般設置有點類似於Form(減去數據庫的東西)。這有點過時(2006年),但我認爲基本原則仍然適用。如果你是新手,元類的東西可能會有點混亂。

0

要澄清一些其他答案:

該字段是,必須是類變量。他們通過元類來完成各種事情,這是定義它們的正確方法。

但是,您的filters變量不需要是類var。它可以很容易地成爲一個實例var - 只需從類中刪除定義並將其放入__init__即可。或者,也許更好,不要把它變成財產 - 只是__init__內的一個本地變量。然後,而不是追加到filters.choices,只需重新分配它。

def __init__(self, *args, **kwargs): 
     super(forms.Form, self).__init__(*args, **kwargs) 
     filters = [] 
     filters.append(PatientFilter('All')) 
     filters.append(PatientFilter('Assigned', 'service__isnull', False)) 
     filters.append(PatientFilter('Unassigned', 'service__isnull', True)) 

     self.fields["filter"].choices = [(i, f.name) for i, f in enumerate(filters)]