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: output

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:

enter image description here

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.

Back to Top