Reputation: 55
I've built dynamic forms using django formsets and javascript, but unfortunately on submitting the form only the first form is submitted. I'd like all dynamically added forms to be submitted also. Any help would be appreciated. Views:
def routestepinfo(request):
class RequiredFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
RouteStepFormSet = formset_factory(RouteStepForm, formset=RequiredFormSet, extra=1, can_order=False, can_delete=True)
if request.method == 'POST':
formset = RouteStepFormSet(request.POST)
if formset.is_valid():
for form in formset.forms:
form.save()
print 'apple'
return redirect("/buildpage/")
else:
formset = RouteStepFormSet()
return render(request, "buildpage/routestepinfo.html", {'formset' :formset})
HTML
<form id= "myForm" method = 'POST' action="{% url 'buildpage:routestepinfo' %}" enctype="multipart/form-data">{% csrf_token %}
{{ formset.management_form }}
<div id="form_set">
{% for form in formset %}
<table class='no_error'>
<tbody>.
{{form.as_table}}
</tbody>
</table>
{% endfor %}
</div>
<p><input type = "button" value = "Add another step" id = "add_more" ></p>
<div id="empty_form" style="display:none">
<table class='no_error'>
{{ formset.empty_form.as_table }}
</table>
</div>
<div id="forms"></div>
<p> </p>
<p> </p>
<input type = "submit" name = "Submit Steps">
</form>
JS Clone:
<script>
$('#add_more').click(function() {
var form_idx = $('#id_form-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});
</script>
Upvotes: 0
Views: 843
Reputation: 55
Fixed it ! Only took a week. Here's how it worked eventually. Views are largely unmodified, just added commit=False to save the list.
def routestepinfo(request, slug=None):
RouteStepFormSet = formset_factory(RouteStepForm, formset = RequiredFormSet, extra=1)
if request.method == 'POST':
formset = RouteStepFormSet(request.POST)
print formset
if formset.is_valid():
for form in formset.forms:
form_item = form.save(commit=False)
print form
form_item.save()
messages.success(request, "Record created")
return redirect ("/buildpage/")
else:
formset = RouteStepFormSet()
return render(request, "buildpage/routestepinfo.html",{'formset':formset})
Javascript was the main thing that changed, with quite a few additions and quite a bit of SO help. The prefix was the first issue, and that was fixed by using formset.empty_form and modifying it from there. Another vital part was updating the total forms, which fixed the saving issue.
<script>
$(document).ready(function() {
$('.add-item').click(function(ev) {
ev.preventDefault();
var count = $('#items-form-container').children().length;
var tmplMarkup = $('#item-template').html();
var compiledTmpl = tmplMarkup.replace(/__prefix__/g, count);
$('div#items-form-container').append(compiledTmpl);
// update form count
$('#id_form-TOTAL_FORMS').attr('value', count+1);
$('html, body').animate({
scrollTop: $("#add-item-button").position().top-200
}, 800);
});
});
</script>
Finally the html. This combined some of my own stuff with a very helpful SO post(forgot the question it came from), where formset is generated as an empty form and cloned from there.:
<div type="text/html" id="item-template">
<table>
{{ formset.empty_form.as_table }}
</table>
</div>
<font face = Flexo>
<form id= "myForm" method = 'POST' action="{% url 'buildpage:routestepinfo' %}" enctype="multipart/form-data">{% csrf_token %}
{{ formset.management_form }}
<div id="items-form-container">
{% for form in formset.forms %}
<div id="item-{{ forloop.counter0 }}">
<table>
{{form.as_table}}
<table>
</div>
{% endfor %}
</div>
<a href="#" id="add-item-button" class="btn btn-info add-item">Add Step</a>
<input type = "submit" name = "Submit Steps">
</form>
Hope this helps some people. Cheers
Upvotes: 1