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.