2010-10-14 20 views
6

donné une classe de formulaire (quelque part au fond de votre application géant Django) ..Singe patcher une classe de formulaire Django?

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

Et étant donné que vous voulez ajouter un autre champ à cette forme sans étendre ou de modifier la classe de forme elle-même, pourquoi ne pas le suivant le travail d'approche?

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

(Ma première hypothèse est que le hack metaclass que Django utilise applique uniquement la première fois Si oui, la classe de forme est construite. Y aurait-il un moyen de redéclarer la classe pour surmonter cela?)

+0

Vous avez presque certainement raison. C'est exactement la raison pour laquelle vous ne pouvez pas facilement ajouter de nouveaux champs à une sous-classe models.Model. –

+0

Avec les modèles, il y a le problème "syncdb" même si le correctif de singe a fonctionné. Mais avec des formes de réparation de singe pourrait être un épargnant de vie à certains moments à mon humble avis. –

Répondre

7

Certaines définitions pertinentes se trouvent dans django/forms/forms.py. Ils sont:

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

get_declared_fields est appelé à partir DeclarativeFieldsMetaclass et construit une liste avec les instances de terrain triées par leur compteur de création. Il ajoute ensuite les champs des classes de base à cette liste et renvoie le résultat sous la forme d'une instance OrderedDict avec le nom du champ servant de clés. DeclarativeFieldsMetaclass puis colle cette valeur dans l'attribut base_fields et appelle type pour construire la classe. Il passe ensuite la classe à la fonction media_property dans widgets.py et attache la valeur de retour à l'attribut media sur la nouvelle classe.

media_property renvoie une méthode de propriété qui reconstruit les déclarations de média sur chaque accès. Mon sentiment est que ce ne sera pas pertinent ici, mais je peux me tromper.

En tout cas, si vous n'êtes pas déclarez un attribut Media (et aucune des classes de base ne), il retourne uniquement une nouvelle Media instance sans argument au constructeur et je pense que monkeypatching un nouveau domaine sur devrait être aussi simple que d'insérer manuellement le champ dans base_fields.

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

Chaque instance de formulaire obtient alors un deepcopy de base_fields qui devient form_instance.fields dans la méthode __init__ de BaseForm. HTH.

+0

Merci beaucoup. Semble fonctionner comme un charme. –

+0

+1 Le point à OrderedDict m'amène à SortedDict. Je ne sais pas quelles sont les différences, mais ils ont tous deux travaillé pour mon problème (je ne sais pas sur les PO). Merci. – jrhorn424