猴子补丁Django表单类?

7 投票
1 回答
2242 浏览
提问于 2025-04-16 05:28

假设你有一个表单类(在你庞大的Django应用程序的某个深处)……

class ContactForm(forms.Form):
    name = ...
    surname = ...

而且你想在这个表单里添加一个新的字段,但又不想扩展或修改这个表单类本身,那么为什么下面这种方法不起作用呢?

ContactForm.another_field = forms.CharField(...)

(我猜测Django使用的元类技巧只在第一次构建表单类的时候有效。如果是这样的话,有没有办法重新声明这个类来解决这个问题呢?)

1 个回答

9

django/forms/forms.py 文件中,有一些相关的定义。它们是:

  1. class BaseForm
  2. class Form
  3. class DeclarativeFieldsMetaclass
  4. def get_declared_fields

get_declared_fields 是从 DeclarativeFieldsMetaclass 调用的,它会创建一个字段实例的列表,并按照创建的顺序进行排序。然后,它会把基类中的字段添加到这个列表的前面,并将结果作为一个 OrderedDict 实例返回,字段名作为键。接着,DeclarativeFieldsMetaclass 会把这个值放到 base_fields 属性中,并调用 type 来构建类。然后,它会把这个类传递给 widgets.py 中的 media_property 函数,并将返回的值附加到新类的 media 属性上。

media_property 返回一个属性方法,每次访问时都会重新构建媒体声明。我觉得在这里可能不太相关,但我也可能错了。

无论如何,如果你没有声明 Media 属性(而且基类也没有),那么它只会返回一个新的 Media 实例,构造函数没有任何参数。我认为在 base_fields 中手动插入字段应该很简单。

ContactForm.another_field = forms.CharField(...)
ContactForm.base_fields['another_field'] = ContactForm.another_field

每个表单实例会在 BaseForm__init__ 方法中获得一个 base_fields 的深拷贝,这个拷贝会变成 form_instance.fields。希望这对你有帮助。

撰写回答