猴子补丁Django表单类?
假设你有一个表单类(在你庞大的Django应用程序的某个深处)……
class ContactForm(forms.Form):
name = ...
surname = ...
而且你想在这个表单里添加一个新的字段,但又不想扩展或修改这个表单类本身,那么为什么下面这种方法不起作用呢?
ContactForm.another_field = forms.CharField(...)
(我猜测Django使用的元类技巧只在第一次构建表单类的时候有效。如果是这样的话,有没有办法重新声明这个类来解决这个问题呢?)
1 个回答
9
在 django/forms/forms.py
文件中,有一些相关的定义。它们是:
class BaseForm
class Form
class DeclarativeFieldsMetaclass
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
。希望这对你有帮助。