Django ModelFormset_Factory issue with rendering form
I've reviewed other questions and answers, worked through the django documentation and followed tutorials but I can't get my model formset to render correctly.
So far, I have the following working fine:
forms.py
class BulkUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(BulkUpdateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_method = "post"
self.helper.layout = Layout(
Row(
Column("slug"),
),
Row(
Column("registered"),
Column("start_date"),
Column("end_date"),
Column("achieved"),
Column("pfr_start"),
Column("pfr_end"),
),
)
self.fields["slug"].label = ""
class Meta:
model = LearnerInstance
fields = ["id","slug","registered","start_date","end_date","achieved","pfr_start","pfr_end",
]
widgets = {
"slug": forms.TextInput(
attrs={
"readonly": True,
},
),
}
template
<div class="container d-flex align-items-center flex-column pt-1">
<div class="card text-dark bg-light mb-3 p-3" style="max-width: 95rem;">
<br>
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{% crispy form %}
<hr>
{% endfor %}
</div>
<input type="submit" name='registered', class="btn btn-primary" value="Update">
</form>
</div>
views.py
def bulk_update(request):
context = {}
selected_rows = request.POST.getlist("select") #posted from previous page with a table and checkboxes to select the queryset I need to update
BulkFormset = modelformset_factory(
LearnerInstance, form=BulkUpdateForm, edit_only=True, extra=0)
queryset = LearnerInstance.objects.filter(id__in=selected_rows)
formset = BulkFormset(queryset=queryset)
context["formset"] = formset
return render(request, "learner/bulk_update.html", context)
Results in the following output, which is what I hope to see:
However - when I add request POST
BulkFormset = modelformset_factory(
LearnerInstance, form=BulkUpdateForm, edit_only=True, extra=0
)
queryset = LearnerInstance.objects.filter(id__in=selected_rows)
if request.method == "POST":
formset = BulkFormset(
request.POST,
queryset=queryset,
)
if formset.is_valid():
formset.save()
else:
formset = BulkFormset(queryset=queryset)
context["formset"] = formset
I see the following:
Please could someone help me understand where I've gone wrong? I have tried to resolve this myself but always come to the same issue as soon as the post request is added to the view.
When you submit the formset, Django creates a new POST
request that doesn't contain the original selected_rows
data from your previous page. This causes the queryset to be empty, so no forms are rendered in the formset.
You can add hidden inputs to your template to preserve selected row IDs like this in your form:
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
<!-- Add these hidden inputs -->
{% for row_id in selected_rows %}
<input type="hidden" name="selected_rows" value="{{ row_id }}">
{% endfor %}
{% for form in formset %}
{% crispy form %}
<hr>
{% endfor %}
<input type="submit" name="registered" class="btn btn-primary" value="Update">
</form>
Next, update your view to handle both GET and POST
def bulk_update(request):
# Get selected rows from GET or POST
if request.method == "GET":
selected_rows = request.GET.getlist("select")
else:
selected_rows = request.POST.getlist("selected_rows")
BulkFormset = modelformset_factory(
LearnerInstance, form=BulkUpdateForm, edit_only=True, extra=0)
queryset = LearnerInstance.objects.filter(id__in=selected_rows)
if request.method == "POST":
formset = BulkFormset(request.POST, queryset=queryset)
if formset.is_valid():
formset.save()
else:
formset = BulkFormset(queryset=queryset)
context = {
"formset": formset,
"selected_rows": selected_rows # Add this
}
return render(request, "learner/bulk_update.html", context)
By passing the selected row IDs through hidden form fields they will persist across form submissions.