[Fixed]-How do you update a Django Form Meta class fields dynamically from the form constructor?


No, that won’t work. Meta is parsed by – surprisingly – the metaclass, before you even get to __init__.

The way to do this is to add the field manually to self.fields:

def __init__(self, *args, **kwargs):
    super(PartialAuthorForm, self).__init__(*args, **kwargs)
    self.fields['year'] = forms.CharField(whatever)


The answers from others are are great (especially @BranHandley’s), but sometimes, its easiest to conditionally remove something instead. Obviously this doesn’t work with things that are dynamically created, but if you only need to optionally include a field or not, then try it like this.

class MyModelForm(ModelForm):
  class Meta:
    model = MyModel
    fields = ['normalField', 'conditionalField', ]

  def __init__(self, *args, **kwargs):

    super(MyModelForm, self).__init__(*args, **kwargs)

    if condition is True:
      del self.fields['conditionalField']

Doing it the other way can be rather complicated as pointed out above, but dropping it conditionally is pretty straight forward. =)


The answers that say to use __init__ will work but you lose the connection to the model (assuming that the field you are adding is on the model). Instead you can make a new form dynamically that inherits from the existing form.

def my_form_factory:
    class ModifiedAuthorForm(PartialAuthorForm):
        class Meta(PartialAuthorForm.Meta):
            fields = PartialAuthorForm.Meta.fields
            if hasattr(fields, 'append'):
                fields = fields + ('year',)
return ModifiedAuthorForm

Of course if you want to be robust you should probably check that the field you are adding isn’t already in fields and you should probably handle exclude as well.


Adding field to model form by overriding form’s __init__ method will not work as model form fields are initialized before form constructor call. So you won’t get any values from actual model to the form and your values will not be saved to db after save call.

You need to use full beauty of python and use class decorator like this 😉

def year_decorator(form_class):
    class ModifiedForm(form_class):
        class Meta(form_class.Meta):
            if isinstance(form_class.Meta.model, Author):
                fields = form_class.Meta.fields + ('year',)
    return ModifiedForm

class PartialAuthorForm(ModelForm):
    class Meta:
         model = Author
         fields = ('name', 'title')


The inner Meta class is already processed when the ModelForm class is created through a metaclass, which is BEFORE an instance is created. Doing what you ware trying to do in __init__ will not work like that!
You can access the created field objects in a dictonary self.fields eg. you can do something like self.fields['year'] = forms.MyFormField(...).

Leave a comment