为什么Django使用BaseForm?

2 投票
2 回答
1920 浏览
提问于 2025-04-16 00:49

我想我终于明白了,他们需要使用这个 DeclarativeFieldsMetaclass(这个东西可以把类的字段变成实例变量,并且保持它们的顺序,使用一个有序的字典)。不过,我还是不太明白他们为什么选择用 BaseForm,而不是直接在 Form 类里实现所有功能呢?

他们在这里 留下了一个评论

class Form(BaseForm):
    "A collection of Fields, plus their associated data."
    # This is a separate class from BaseForm in order to abstract the way
    # self.fields is specified. This class (Form) is the one that does the
    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    # to define a form using declarative syntax.
    # BaseForm itself has no way of designating self.fields.

但是我真的不太理解。评论里说“为了抽象化 self.fields 的指定方式”——可是 Python 在调用 DeclarativeFieldsMetaclass.__new__ 之前就会调用 Form.__init__,所以他们本来可以直接在 Form.__init__ 里充分利用 self.fields;那为什么还需要多一层抽象呢?

2 个回答

2

我觉得原因很简单,单靠 BaseForm 是无法用声明式语法来定义字段的,也就是说,不能直接用一种简单的方式来写字段。

class MyForm(Form):
    field_xxx = form.TextField(...)
    field_nnn _ form.IntegerField(...)

要实现这样的功能,就需要一个名为 DeclarativeFieldsMetaclass 的元类,这个元类只在表单中设置。他们这样做是因为

这是一个与 BaseForm 分开的类, 目的是为了抽象化, self.fields 的具体定义方式。

所以现在你可以写一个 WierdForm 类,在这个类中可以用一些奇怪的方式来定义字段,比如给类对象传参数。关键是所有的 API 都在 BaseForm 中,而 Form 类只是提供了一种简单的方式来定义字段。

总结:在我看来,Django 更倾向于引入另一层,这样如果需要的话,可以实现不同类型的字段声明,至少它把表单的非核心功能分开了。

1

来源:

class MetaForm(type):
    def __new__(cls, name, bases, attrs):
        print "%s: %s" % (name, attrs)
        return type.__new__(cls, name, bases, attrs)

class BaseForm(object):
    my_attr = 1
    def __init__(self):
        print "BaseForm.__init__"

class Form(BaseForm):
    __metaclass__ = MetaForm
    def __init__(self):
        print "Form.__init__"

class CustomForm(Form):
    my_field = 2
    def __init__(self):
        print "CustomForm.__init__"

f = CustomForm()

输出:

Form: {'__module__': '__main__', '__metaclass__': <class '__main__.MetaForm'>, '__init__':<function __init__ at 0x0227E0F0>}
CustomForm: {'__module__': '__main__', 'my_field': 2, '__init__': <function __init__ at 0x0227E170>}
CustomForm.__init__

看起来 MetaForm.__new__ 被调用了两次。一次是为了 Form,另一次是为了 CustomForm,但从来没有为 BaseForm 调用过。通过保持一个干净(空的) Form 类,就不会有多余的属性需要遍历。这也意味着你可以在 BaseForm 中定义一些 Fields,这些字段可以用于内部使用,但不会被渲染出来。

撰写回答