Monday, February 21, 2011

Is it possible to add a single row to a Django form?

I have a form along the lines of:

class aForm(forms.Form):
  input1 = forms.CharField()
  input2 = forms.CharField()
  input3 = forms.CharField()

What I'm hoping to do is add an additional input3 to the form when the user clicks an "add another input3".

I've read a bunch of ways that let me add new forms using formsets, but I don't want to append a new form, I want to just add a new input3.

Is this possible?

From stackoverflow
  • I would define input3 in your form definition, not require it, and then hide it by default. The user can then show the input using JavaScript.

    If you want to allow the user to add an undefined amount of additional inputs, I would look further into formsets.

  • Perhaps something like this:

    from django import forms
    from django.utils.datastructures import SortedDict
    def some_view(request):
        data = {}
    
        # Using session to remember the row count for simplicity
        if not request.session.get('form_rows', None):
            request.session['form_rows'] = 3
    
        # If posted with add row button add one more row
        if request.method == 'POST' and request.POST.get('rows', None):
                request.session['form_rows'] += 1
                data = request.POST
    
        # Create appropriate number of form fields on the fly
        fields = SortedDict()
        for row in xrange(1, request.session['form_rows']):
            fields['value_{0}'.format(row)] = forms.IntegerField()
    
        # Create form class
        MyForm = type('MyForm', (forms.BaseForm,), { 'base_fields': fields })
    
        form = MyForm(initial=data)
    
        # When submitted...
        if request.method == 'POST' and not request.POST.get('rows', None):
            form = MyForm(request.POST)
            if form.is_valid():
                # do something
        return render_to_response('some_template.html', {
            'form':form,
        }, context_instance=RequestContext(request))
    

    With template:

    <form action="" method="post">
        {{ form }}
        <input type="submit" />
        <input type="submit" value='Add' name='rows' />
    </form>
    

    This is over simplified but it works.

    You could easily modify this example so you can make request using AJAX and just replace old form with new one.

  • for adding dynamic fields overwrite the init method.

    Something like this:

    class aForm(forms.Form):
      input1 = forms.CharField()
      input2 = forms.CharField()
    
      def __init__(self, addfields=0, *args, **kwargs):
        super(aForm, self).__init__(*args, **kwargs)
    
        #add new fields
        for i in range(3,addfields+3)
           self.fields['input%d' % i] = forms.CharField()
    

0 comments:

Post a Comment