为什么Django启动时会调用表单字段的__init__方法?

3 投票
2 回答
2140 浏览
提问于 2025-04-17 10:42

我有一个Django应用,里面有一些自定义的表单字段,这些字段在创建的时候会执行一些比较慢的操作。最近我发现,当Django启动的时候,这些字段的创建方法就会被调用,甚至在用户还没做任何需要这个表单的操作之前。

为什么在服务器启动时就会创建这些字段呢?

举个例子:

urls.py:

from myapp.views import view1
...
url(r'^test$', view1.test),

views/view1.py:

class MyForm(ModelForm):
    class Meta:
        model = MyModel
    field1 = MyChoiceField()

class MyChoiceField(ChoiceField):
    def __init__(self, choices=(), required=True, widget=None, label=None,
             initial=None, help_text=None, *args, **kwargs):
    super(ChoiceField, self).__init__(required, widget, label, initial,
                                      help_text, *args, **kwargs)     
    self.choices = [(m.id, m.name) for m in ReallyLargeTableModel.objects.all()]     

如果我在这个字段的创建方法里设置一个断点,然后启动Django,第一次请求任何页面的时候就会停下来,即使那个页面并不需要这个表单或字段。错误追踪信息显示是从urls.py的导入行开始的。

这是因为我在urls.py里导入了view1,而不是导入view1.test吗?

补充:这并不是Django特有的,这里有一个测试案例来说明这个行为:

class Something():
    def __init__(self):
        print "Something __init__() called"

class UsesSomething():
    field = Something()

如果你在交互式终端运行这个代码,它会打印出“Something init() called”。这让我很惊讶,因为我实际上并没有创建一个UsesSomething对象。

2 个回答

1

在你的例子中:

class UsesSomething():
    field = Something()

这行代码 field = Something() 会在你导入这个模块时执行,因为Python在处理类定义的时候就是这样工作的。其实你可以在类定义里面放任何代码。

模块:test.py:

class UsesSomething():
    print "wow!"

>>> import test
wow!
11

因为你在表单定义中实例化了字段,这个表单很可能是被你某个视图导入的。

字段的初始化不是进行这种动态初始化的合适地方,正是因为这个原因。你需要的是在表单初始化时调用的东西,也就是表单的 __init__ 方法。

不过,实际上你根本不需要这样做——你只需要使用 forms.ModelChoiceField,它会接收一个查询集,并为你动态分配选项。

class MyForm(ModelForm):
    field1 = forms.ModelChoiceField(queryset=ReallyLargeTableModel.objects.all())

撰写回答