Hello,
I have made an inline formset, if i render the form as seen below the data get saved, even if I add new fields to the formset.
<form action="" method="POST">
{{ form.management_form }}
{% for forms in form %}
{% csrf_token %}
<p>
{{ forms }}
</p>
{% endfor %}
<input type="submit">
</form>
On the other hand if I do this (seen below) the form does not save and throws me my error, as seen in views.py.
<form action="" method="POST">
{{ form.management_form }}
{% for forms in form %}
{% csrf_token %}
<p>
{{ forms.direction }}
</p>
<p>
{{ forms.ticker }}
</p>
<p>
{{ forms.volume }}
</p>
<p>
{{ forms.price }}
</p>
<p>
{{ forms.transaction_date }}
</p>
<p>
{{ forms.DELETE }}
</p>
{% endfor %}
<input type="submit">
</form>
views.py:
@login_required
def transactions(request, portfolio_id):
portfolioen = Portfolio.objects.get(pk=portfolio_id)
TransactionFormSet = inlineformset_factory(Portfolio, Transactions, form=TransactionForm, extra=5, can_delete=True)
if request.method == ‘POST’:
formset = TransactionFormSet(request.POST, instance=portfolioen)
if formset.is_valid():
formset.save()
messages.success(request, 'Transactions added to portfolio')
return redirect('transactions', portfolio_id=portfolioen.id)
else:
messages.error(request,'ERROR!')
formset = TransactionFormSet(instance=portfolioen)
context = {
'form' : formset,
'portfolio':portfolioen
}
return render(request, 'backapp/transaction.html', context)
Anyone who know why this is happening, and what I can do to fix this?
Also, the data only gets saved the first time
The first thing I’d check is to look at the rendered HTML through your browser’s developer tools to verify that all the fields being rendered in the first case are being rendered in the second.
Also, it would be helpful if you include the error itself being thrown - Not just your error message, but iterate through the messages associated with the formset.
Ken
Hey Ken, I actually did:
if request.method == 'POST':
formset = TransactionFormSet(request.POST, instance=portfolioen)
if formset.is_valid():
formset.save()
messages.success(request, 'Transactions added to portfolio')
return redirect('transactions', portfolio_id=portfolioen.id)
else:
print('ERROR', formset.errors)
messages.error(request,'ERROR')
In my views.py and got error: ERROR [{‘id’: [‘This field is required.’]}, {}, {}, {}, {}, {}]
Also I have checked the rendered HTML and seems to be no errors there. The odd thing is that the first time form is posted, the data get stored
1 Like
I’m not saying there are errors in the HTML - I’m saying there’s a field that you’re not providing when you manually render the fields.
Compare all the fields between the two versions in the rendered HTML, not in the template or the code - you’re missing one in your manual rendering, specifically the id field.
Also, if you’re manually managing the management form, you’ve got a value wrong somewhere. (If you’re not manipulating the management form yourself at all, ignore this comment.)
Ken
I tested both versions, before and after submitting the form.
What i noticed is that both forms have a hidden input:
<input type="hidden" name="transactions_set-0-portfolio" value="84" id="id_transactions_set-0-portfolio">
I’m not manually manipulating the management form.
So in theory both forms should be submitted the same way? What can I do to solve this?
Problem solved, needed to add:
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
4 Likes
Thanks man, I was strugging to solve this issue for a while. Now I able to solve it.
Still fails for me. Can you paste ur entire template if possible?
Thank you sooo much! I was struggling with the same problem.
Please share your entire template file. It would help a lot.
Is it required to put display hidden fields in the child form too? How?
Thanks. Problem solved.
If someone still doesn’t get it right after adding hidden fields, please note the following:
- Add management form to the inline form (child form) eg. {{childformset.management_form}}
- Add hidden fields to the inline form (child form) just after you start iterating in the for loop. The id of the child form is rendered as a hidden field and django cant save unless you provide the hidden fields.
The code that worked for me:
form tag start------
{% csrf_token %}
{{ form.management_form }}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{{ form.non_field_errors }}
…
…
{{ purchaseorderdetail.management_form }}
{% for forms in purchaseorderdetail %}
{% csrf_token %}
{% for hidden in forms.hidden_fields %}
{{ hidden }}
{% endfor %}
{{ purchaseorderdetail.non_field_errors }}
<tr>
<td>{{ forms.rawmaterial }}</td>
<td>{{ forms.orderedqty }}</td>
<td>{{ forms.unit }}</td>
<td>{{ forms.unitprice }}</td>
<td>{{ forms.subtotal }}</td>
</tr>
{% endfor %}
Minor note here - I personally like to include hidden fields within my table, when displaying inlines as a table. For instance:
<tr id="foo" class="bar">
{% for field in formset %}
{% if field.is_hidden %}
<td style="display:none;">{{field}}</td>
{% else %}
<td>{{field}}</td>
{% endif %}
{% endfor %}
</tr>
Then in the header, you can have matching:
<thead>
<tr>
<th style="display: none">id</th>
....
for the columns that need to be hidden.
The upside, if you are using jquery, is that then any info you need to fetch is contained within the same row for the object. Whereas if you need to refer to a hidden field that’s just dumped outside of that format, you need a different selector and things get messier (imo).
Desti_formset = inlineformset_factory(Lass, Desti_antal,
fields=(‘field1’,
‘field2’
),
form=Desti_up_form,
can_delete=True,
extra=4)
If I don’t use field 2 in my template I cant save.