Search code examples
djangoloopsformsetsvalidationerror

Django looping formsets cause ValidationError in management form


I am trying to output two same forms and save them to database with different prefix. I used this post https://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ as an example. However I get validation error that management form is being tampered with. Could you please kindly advise how to solve it? Thank you.

Also is it possible to filter database by the prefix in this case if i want to retrieve the data later for analysis.

VIEWS.PY

from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions

model_names = ['Form1', 'Form2']

def get_assumptions(request):

    AssumptionsFormset = modelformset_factory(
    Assumptions, form=AssumptionsForm, extra=5)

    if request.method == 'POST':

        formsets = [AssumptionsFormset(request.POST, prefix=thing) for thing in model_names]

        if all([formset.is_valid() for formset in formsets]):

            for formset in formsets:

                for form in formset:

                    form.save()

    else:

        formsets = [AssumptionsFormset(request.POST, prefix=thing) for thing in model_names]

    return render(request, 'assumptions.html', {'formsets': formsets})

ASSUMPTIONS.HTML

<div class="form">
  <form action="" method="post">
  {% csrf_token %}
  {% for formset in formsets %}
  {{ formset.management_form }}
  {{ formset.non_form_errors.as_ul }}
  <h1>{{formset.prefix}}</h1>
  <table id="formset" class="form">
  {% for form in formset.forms %}
    {% if forloop.first %}
    <thead><tr>
      {% for field in form.visible_fields %}
      <th>{{ field.label|capfirst }}</th>
      {% endfor %}
    </tr></thead>
    {% endif %}
    <tr class="{% cycle 'row1' 'row2' %}">
    {% for field in form.visible_fields %}
      <td>
      {# Include the hidden fields in the form #}
      {% if forloop.first %}
        {% for hidden in form.hidden_fields %}
        {{ hidden }}
        {% endfor %}
      {% endif %}
        {{ field.errors.as_ul }}
        {{ field }}
      </td>
    {% endfor %}
    </tr>
  {% endfor %}
  </table>
  <input type="submit" value="Submit">
  {% endfor %}
  </form>
  </div>

MODELS.PY

from django.db import models
from django.forms import ModelForm

class Assumptions(models.Model):

    Worst = models.FloatField(null=True, blank=True, default=None)
    Base = models.FloatField(null=True, blank=True, default=None)
    Best = models.FloatField(null=True, blank=True, default=None)

FORMS.PY

from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions

class AssumptionsForm(ModelForm):

    class Meta:
        model = Assumptions
        fields = '__all__'

Solution

  • You are trying to initialize the formsets with request.POST on GET requests, which of course can't work.

    Replace the second

    formsets = [AssumptionsFormset(request.POST, prefix=thing) for thing in model_names]
    

    with

    formsets = [AssumptionsFormset(prefix=thing) for thing in model_names]