Code


Version 3 (modified by toddobryan, 6 years ago) (diff)

--

Creating Fields with Dynamic Forms

Imagine you're creating a Django app to deal with, oh, user polls or something silly like that. You could create a form that includes a place for the pollee to enter his/her name, age, and to answer a question.

import django.newforms as forms

class Survey(forms.Form):
    name = forms.CharField(max_length=20)
    age = forms.IntegerField()
    answer = forms.ChoiceField(choices=(('0', 'Not at All'), 
                                        ('1', 'Sometimes'), 
                                        ('2', 'Often'), 
                                        ('3', 'Always')))

When you display this, you'll get a nice form with three places to input information.

But what if you're allowing your users to create their own survey questions, so that you don't know ahead of time how many answers you'll need? Luckily, you can add fields to the form as you're constructing it, in addition to specifying them as class attributes. If you have all your survey questions in a list (let's call it questions just to be confusing), you could create a Survey class that looks like this:

class Survey(forms.Form):
    name = forms.CharField(max_length=20)
    age = forms.IntegerField()

    def __init__(self, questions, *args, **kwargs):
        super(Survey, self).__init__(self, *args, **kwargs)
        # now we add each question individually
        for i, question in enumerate(questions):
             self.fields['question_%d' % i] = forms.ChoiceField(label=question, ...)

When you display this form, you'll get a separate spot to answer each of the questions you passed in, each with its own index so that you can match it to the question. One complication--if you choose to use this method, you have to pass the same questions list each time you create the form, both on the GET when it's empty and on the POST when it has your answers. It is possible to avoid this, but it takes a little more thought when you set up your views and the workflow. (Saving stuff in the session can make things much easier.)

This technique isn't limited to defining new fields. You can also set a field's initial value in the __init__ method, which is really helpful when your forms depend on your models, but don't match up exactly so you can't use the ModelForm framework.