django的Form类是否保持状态?

时间:2009-08-13 22:35:35

标签: python django

我正在用django构建我的第一个表单,我看到了一些我根本没想到的行为。我定义了一个表单类:

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,}))

我做错了什么?

谢谢,皮特

5 个答案:

答案 0 :(得分:7)

这实际上是Python的一个功能,可以吸引很多人。

当您使用filters = []定义类的变量时,在最初定义类时会计算表达式的右半部分。因此,当您的代码首次运行时,它将在内存中创建一个新列表并返回对此列表的引用。因此,每个AssignmentFilterForm实例都有自己的过滤器变量,但它们都将指向内存中的相同列表。要解决此问题,只需将self.filters的初始化移至__init__方法。

大多数情况下,您不会遇到此问题,因为您使用的类型不会存储为参考。数字,布尔值等存储为其值。字符串是通过引用存储的,但字符串是不可变的,这意味着每次更改字符串并返回新引用时,必须在内存中创建新字符串。

指针不会经常出现在脚本语言中,所以一开始就会让人感到困惑。

这是一个简单的IDLE会话示例,用于显示正在发生的事情

>>> 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']

答案 1 :(得分:1)

您将附加到PER-CLASS变量self.filters。通过在self.filters = []开头__init__,将其变为PER-INSTANCE变量。

答案 2 :(得分: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))

清除选择有效,但肯定会导致线程问题。

答案 3 :(得分:0)

如上所述,您需要将过滤器初始化为实例变量:

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

如果您想了解有关Form类如何工作的更多信息,您应该在Django wiki中阅读此页面:

它讨论了Model类的内部,但是你会发现字段的一般设置有点类似于Form(减去数据库的东西)。它有点过时(2006年),但我认为基本原则仍然适用。如果你是新手,那么元类的东西可能有点令人困惑。

答案 4 :(得分: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)]