1

I have a form which has more than 10 fields. Now i want a particular field, lets say "requirements". This can be more than one requirement, i can deal with that using rich text editor as ask my user to input all the requirements as a ordered list. But for better user experience i am asking this !

I am gonna keep a button under "requirements" field, so that user can click on this button to get a new field. By this, i want all fields to be combined in a dict like

requirements = {'field1','extrafield1'} and etc

How to perform this ? I cant user formset ( as am just adding dynamic field not whole form )

How to deal this with django forms ?

sixovov947
  • 205
  • 1
  • 7

1 Answers1

0

I had a similar problem and came up with following solution (it's based on this answer mainly, but I was able to avoid any javascript):

form contains hidden field to store count of requirement fields; by default form has one such field, but pressing button "add_requirement" increases this count in TestView.post(). On __init__ form adds new fields with proper indexes, if needed, and on save() it goes through all requirement indexes to collect values from respective fields. TestView.post() works differently for form's two buttons: "add_requirement" just increases fields count and renders form again with added field; default "submit" button saves valid form and redirects to success_url or re-displays invalid one.

forms.py

class SimpleForm(forms.Form):   
 
    requirements_count = forms.CharField(widget=forms.HiddenInput(), initial=1)     
    stable_field = forms.CharField(label='Stable field', required=False)     
    other_stable_field = forms.CharField(label='Other field', required=False)
    requirements_1 = forms.CharField(label='Requirements', required=False)
    
    def __init__(self, *args, **kwargs):   
        super(SimpleForm, self).__init__(*args, **kwargs)
        if self.is_bound:
            requirements_count = int(self.data.get('requirements_count', 1))
        else:
            requirements_count = int(self.initial.get('requirements_count', 1))
        if requirements_count > 1:   
            for index in range(2, requirements_count + 1):
                self.fields.update(
                    {'requirements_' + str(index): 
                    forms.CharField(label='Requirements', required=False)}
                    )
                
    def save(self, *args, **kwargs):
        form = self.cleaned_data
        requirements_count = int(form.get('requirements_count', 1))
        requirements_list = []
        for index in range(1, requirements_count + 1):
            requirements = form.get('requirements_' + str(index), '')
            if requirements:            
                requirements_list.append(requirements)

views.py

class TestView(FormView):
    template_name = 'testtemplate.html'
    form_class = SimpleForm
    form = None
    success_url = reverse_lazy('home')
        
    def post(self, request, *args, **kwargs):
        request_POST = request.POST
        requirements_count = int(request_POST.get('requirements_count', 1))
        if 'add_requirement' in request_POST:
            new_initial = get_initial(request_POST)
            new_initial['requirements_count'] = requirements_count + 1
            self.form = SimpleForm(initial=new_initial)
            context = self.get_context_data(form=self.form)
            response = render(request, self.template_name, context)
        else:    
            self.form = SimpleForm(data=request_POST)
            context = self.get_context_data(form=self.form)
            if self.form.is_valid():
                response = self.form_valid(self.form)
            else:
                response = render(request, self.template_name, context)  
        return response
        
    def form_valid(self, form):
        form.save()
        return super().form_valid(self.form)

# helper function
def get_initial(previous_data):
    new_initial = {}
    for key, value in previous_data.items():
        if value != '' and key != 'csrfmiddlewaretoken':
            new_initial.update({key: value})
    return new_initial

testtemplate.html

<form action="" method="post">
  {% csrf_token %}
  <table border="1">
    {{ form.as_table }}
  </table>
  <input type="submit">
  <input type="submit" name="add_requirement" value="Add another requirement">
  {{ form.non_field_errors }}
</form>

I'm in no way Django expert and it may be not the best solution, but it works for me and is relatively simple.

Margo Grig
  • 11
  • 2