8

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?

5
  • You certainly shouldn't be inheriting from BaseModelForm. Commented Dec 16, 2014 at 12:01
  • BaseModelForm is just a class I use to deal with labels, why shouldn't I use it? Commented Dec 16, 2014 at 12:05
  • perhaps self.fields[p.title] = forms....? assuming that p.title is a string Commented Dec 16, 2014 at 12:07
  • @MihaiZamfir omg, of course, what was I thinking, cheers mate, add it as a response and I'll accept it but I assume this question is going to be very redundant Commented Dec 16, 2014 at 12:11
  • 1
    Apologies, I thought it was forms.BaseModelForm. Commented Dec 16, 2014 at 14:10

2 Answers 2

6

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 ) !

Sign up to request clarification or add additional context in comments.

2 Comments

But then how to gain advantage of all the things ModelForm provides?
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...
5

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))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.