修补Django表单类的猴子?

utk*_*tas 6 python django monkeypatching django-forms

给定一个表单类(在您的巨型Django应用程序的某个深处)

class ContactForm(forms.Form):
    name = ...
    surname = ...
Run Code Online (Sandbox Code Playgroud)

并且考虑到您希望在不扩展或修改表单类本身的情况下向此表单添加另一个字段,为什么以下方法不起作用?

ContactForm.another_field = forms.CharField(...)
Run Code Online (Sandbox Code Playgroud)

(我的第一个猜测是Django使用的元类hackery仅在第一次构造表单类时应用.如果是这样,是否有办法重新声明类以克服这个问题?)

aar*_*ing 8

一些相关的定义出现在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构造类.然后它将类传递给media_property函数,widgets.py并将返回值附加到media新类的属性上.

media_property返回一个属性方法,用于在每次访问时重建媒体声明.我的感觉是它不相关,但我可能是错的.

无论如何,如果你没有声明一个Media属性(并且没有基类做),那么它只返回一个Media没有参数的新实例,我认为monkeypatching一个新字段应该像手动插入一样简单.进入base_fields.

ContactForm.another_field = forms.CharField(...)
ContactForm.base_fields['another_field'] = ContactForm.another_field
Run Code Online (Sandbox Code Playgroud)

每个表单实例,然后得到一个deepcopybase_fields变成form_instance.fields__init__的方法BaseForm.HTH.