Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically added form fields are removed in form.cleaned_data

I put some client-side Javascript in my template that allows a user to dynamically add fields to a form. My problem is that these fields are cleaned in form.cleaned_data, so I can't access them that way.

All the fields are accessible in request.POST, so I could just solve this problem with that, but I want to do this the "right way" and I think that the solution lies somewhere in using django forms rather than reading the request directly.

I tried overriding form.clean(), but it seems like the data is already gone by the time it gets there.

Other details: I am naming these fields fieldname_x, where x is a number. In request.POST, request.POST['fieldname'] is a list of a all the values, but form.cleaned_data contains only the last value of each list.

like image 664
ty. Avatar asked Nov 27 '10 23:11

ty.


2 Answers

Do you know what type these fields are going to be beforehand? Are they just going to be simple text fields? I've done something similar to this, creating dynamic forms.

# make sure these exist by examining request.POST
custom_fields = ['fieldname_1', 'fieldname_2']

attrs = dict((field, forms.CharField(max_length=100, required=False)) 
             for field in custom_fields)
DynamicForm = type("DynamicForm", (YourBaseForm,), attrs)
submitted_form = DynamicForm(request.POST)

Your submitted form should now contain all the fields you require, along with their values. You might want to remove required=False, but that's up to you.

What this does, is perform a dynamic subclass of your base form, adding the attributes passed in as attrs to the class definition. So when you create an instance with post data, they should be mapped correctly.

Edit:

I read the question a little more closely. What you'll want to do is ensure that your dynamic input elements are named correctly, and the values map to those fieldnames once it reaches django. Otherwise, request.POST will not fill the form in correctly.

<input type='text' name='fieldname_1' value='value_for_field_1' /> 

etc

like image 135
Josh Smeaton Avatar answered Sep 18 '22 13:09

Josh Smeaton


It is also possible to do this work in your form file, here is an excellent demonstration by Jacob Kaplan-Mosse for dynamic forms : http://jacobian.org/writing/dynamic-form-generation/ that applies quite well for this problem.

What is done is adding a methods to you form class that add the extra dynamic fields and then yields the information from the clean so that you can get it in your view.

class MyForm(forms.Form):
    text = forms.CharField(max_length=30)

    def __init__(self, *args, **kwargs):
        extra = kwargs.pop('extra')
        super(MyForm, self).__init__(*args, **kwargs)

        for i, question in enumerate(extra):
            self.fields['fieldname_%s' % i] = forms.CharField(label=question)

    def extra_fields(self):
        for name, value in self.cleaned_data.items():
            if name.startswith('fieldname_'):
                yield (self.fields[name].label, value)

And to call it from the view :

def doSomething(request, extra_fields):
    form = MyForm(request.POST or None, extra=extra_fields)
    if form.is_valid():
        for (question, answer) in form.extra_answers():
            save_answer(request, question, answer)
        return redirect("create_user_success")

return render_to_response("template/form.html", {'form': form})

Very neat, congrats to Jacob Kaplan-Moss

like image 21
Nielluin Avatar answered Sep 19 '22 13:09

Nielluin