Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monkey patching a Django form class?

Given a form class (somewhere deep in your giant Django app)..

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

And considering you want to add another field to this form without extending or modifying the form class itself, why does not the following approach work?

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

(My first guess is that the metaclass hackery that Django uses applies only the first time the form class is constructed. If so, would there be a way to redeclare the class to overcome this?)

like image 995
utku_karatas Avatar asked Oct 14 '10 06:10

utku_karatas


1 Answers

Some pertinent definitions occur in django/forms/forms.py. They are:

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

get_declared_fields is called from DeclarativeFieldsMetaclass and constructs a list with the field instances sorted by their creation counter. It then prepends fields from the base classes to this list and returns the result as an OrderedDict instance with the field name serving as the keys. DeclarativeFieldsMetaclass then sticks this value in the attribute base_fields and calls to type to construct the class. It then passes the class to the media_property function in widgets.py and attaches the return value to the media attribute on the new class.

media_property returns a property method that reconstructs the media declarations on every access. My feeling is that it wont be relevant here but I could be wrong.

At any rate, if you are not declaring a Media attribute (and none of the base classes do) then it only returns a fresh Media instance with no arguments to the constructor and I think that monkeypatching a new field on should be as simple as manually inserting the field into base_fields.

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

Each form instance then gets a deepcopy of base_fields that becomes form_instance.fields in the __init__ method of BaseForm. HTH.

like image 88
aaronasterling Avatar answered Oct 18 '22 14:10

aaronasterling