Add form fields to django form dynamically

10,126

Solution 1

Assuming that p.title is a string variable, then this should work:

if p.type_value == 1:
     # Text
     self.fields[p.title] = forms.CharField(label=p.human_title, required=False))

Solution 2

I recommend creating the form on the fly using type. So, you'll need to create a function that will generate a list of all the fields you want to have in your form and then use them to generate the form, something like this :

def get_form_class():
    flat_properties = FlatProperty.objects.all()
    form_fields = {}
    for p in flat_properties:
        if p.type_value == 1:
        form_fields['field_{0}'.format(p.id)] = django.forms.CharField(...)
        elif p.type_value == 2:
            form_fields['field_{0}'.format(p.id)] = django.forms.IntegerField(...)
        else:
            form_fields['field_{0}'.format(p.id)] = django.forms.BooleanField(...)
    # ok now form_fields has all the fields for our form
    return type('DynamicForm', (django.forms.Form,), form_fields  )

Now you can use get_form_class wherever you want to use your form, for instance

form_class = get_form_class()
form = form_class(request.GET) 
if form.is_valid() # etc 

For more info, you can check my post on creating dynamic forms with django: http://spapas.github.io/2013/12/24/django-dynamic-forms/

Update to address OP's comment (But then how to gain advantage of all the things ModelForm provides?): You can inherit your dynamic form from ModelForm. Or, even better, you can create a class that is descendant of ModelForm and defines all the required methods and attributes (like clean, __init__, Meta etc). Then just inherit from that class by changing the type call to type('DynamicForm', (CustomForm,), form_fields ) !

Share:
10,126

Related videos on Youtube

Alfonso Embid-Desmet
Author by

Alfonso Embid-Desmet

Updated on September 15, 2022

Comments

  • Alfonso Embid-Desmet
    Alfonso Embid-Desmet over 1 year

    Due to a BD design where depending on a value, the data is stored in different cells, I have to add form fields dynamically. I was thinking on this:

    class EditFlatForm(BaseModelForm):
    
    
        on_sale = forms.BooleanField(required=False)
        on_rent = forms.BooleanField(required=False)
    
    
        class Meta:
    
            model = Flat
    
            fields = ('title', 'flat_category', 'description')
    
            ...
    
    
        def __init__(self, *args, **kwargs):
    
            super(EditFlatForm, self).__init__(*args,**kwargs)
    
    
            flat_properties = FlatProperty.objects.all()
    
            for p in flat_properties:
    
                if p.type_value == 1:
                    # Text
                    setattr(self, p.title, forms.CharField(label=p.human_title, required=False))
                elif p.type_value == 2:
                    # Number
                    setattr(self, p.title, forms.IntegerField(label=p.human_title, required=False))
                else:
                    # Boolean
                    setattr(self, p.title, forms.BooleanField(label=p.human_title, required=False))
    

    But the fields don't get added, what am I missing?

  • Alfonso Embid-Desmet
    Alfonso Embid-Desmet over 9 years
    But then how to gain advantage of all the things ModelForm provides?
  • ArtOfWarfare
    ArtOfWarfare over 3 years
    The dynamically created form ends up having a weird package name - if you try this str(type('PostForm2', (Form,), {})) - it returns <class 'django.forms.widgets.PostForm2'> - I feel like if the form is involved in an exception and shows up in a stacktrace, that'll really confuse people...