【问题标题】:Why are form field __init__ methods being called on Django startup?为什么在 Django 启动时调用表单字段 __init__ 方法?
【发布时间】:2012-01-20 16:12:59
【问题描述】:

我有一个带有自定义表单字段的 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 对象。

【问题讨论】:

  • 老实说,从来没有真正关注过,因为它在我的应用程序中并不重要,但感觉不对。 __init__ 在类被实例化之前不应被调用。通常,该类实际上需要您必须提供的额外数据才能运行初始化。不过,如果您放弃导入视图而直接使用 url(r'^test$', 'myapp.views.view1.test'), 会发生什么?

标签: python django


【解决方案1】:

因为您在表单定义中实例化了字段,这可能是由您的一个视图导入的。

由于这个确切的原因,字段 init 是执行这种动态初始化的错误位置。您想要在表单初始化时调用的东西:即表单的__init__

也就是说,您实际上根本不想这样做 - 您只需要使用 forms.ModelChoiceField,它接受一个查询集并为您动态分配选择。

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

【讨论】:

  • 你是对的,但我仍然试图理解为什么在我实际实例化 MyForm 对象之前,导入 MyForm 会调用 ModelChoiceField() 构造函数。查看我的编辑。
  • 因为在类级别声明的东西——即不在__init__ 或其他方法内——是类属性。它们对类可用,而不是对它的任何特定实例可用。类定义是一段可执行代码,因此在定义中调用字段将在定义类时执行该调用,即在第一次导入其模块时。
【解决方案2】:

在你的例子中:

class UsesSomething():
    field = Something()

field = Something() 代码行将在您导入包含模块时执行,因为 Python 处理类定义。这就是 Python 的工作原理。您实际上可以将任意代码放入类定义中。

模块:test.py:

class UsesSomething():
    print "wow!"

>>> import test
wow!

【讨论】:

  • 我已经使用 Python 几年了,但这仍然让我感到惊讶。可能是因为这在 Java 中不会发生。
  • @ChaseSeibert 来自 C++,我也很惊讶。但如果你仔细想想,它是有道理的。通常,您只需在类定义中进行属性分配或函数定义。这些属性是独立于该类的任何实例而存在的类属性。请记住,类是 Python 中的第一级对象,因此类定义本身实际上是在创建一个对象(不是类的实例,而是实际的类对象)。
猜你喜欢
  • 2014-04-18
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 2011-11-06
  • 1970-01-01
  • 2013-01-09
  • 1970-01-01
  • 2012-07-23
相关资源
最近更新 更多