2009-08-13 94 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)]