Help with processing inline formset

Hi,

I have a model called Party. Each party can have multiple ratings from different rating sources. These ratings use a model called PartyRating. PartyRating has four foreign keys to link the PartyRating to a party, a rating source, a rating and an outlook. I’d like to manage the party ratings on the edit page for the party, so I am using inline forests for the PartyRating. When the edit page loads, the formset loads the ratings without issue. However, when saving the PartyRating, I am struggling to get the POST data into the inline formset for validation and saving.

Although I can see the values from the inline formset in the POST data, the inline formset object does not look to be populating and the subsequent is_valid() method fails, though I am not able to produce a failure message from that.

Models.py

class Party(models.Model):
    name = models.CharField(_('Name'), max_length=200, db_comment='Party Name', help_text='Party Name')
    external_comment = models.CharField("External Comment", max_length=1000, blank=True)
    internal_comment = models.CharField("Internal Comment", max_length=1000, blank=True)
    ...

class PartyRating(models.Model):
    party = models.ForeignKey('Party', on_delete=models.PROTECT)
    rating = models.ForeignKey('Rating', on_delete=models.PROTECT)
    rating_source = models.ForeignKey('RatingSource', on_delete=models.PROTECT)
    outlook = models.ForeignKey('Outlook', on_delete=models.PROTECT, null=True)
    created = models.DateTimeField(_('Created'), auto_now_add=True)
    created_by = models.ForeignKey('Person', on_delete=models.PROTECT, related_name='party_rating_created_by', verbose_name=_('Created By'))
    modified = models.DateTimeField(_('Modified'), auto_now=True)
    modified_by = models.ForeignKey('Person', on_delete=models.PROTECT, related_name='party_rating_modified_by', verbose_name=_('Modified By'))
    modified_from = models.CharField(_('Modified From'), max_length=200)
    class Meta:
        unique_together = ('party', 'rating_source',)

Views.py

def partyUpdate(request, partyId):
    party = Party.objects.get(pk=partyId)
    party_form = PartyForm(instance=party)
    party_rating_formset = PartyRatingInlineFormSet(instance=party)
    if request.method == "POST":
        party_form = PartyForm(request.POST, instance=party)
        if party_form.is_valid():
            party_form = get_update_form_defaults(party_form)
            party_form.save()
            party_rating_formset = PartyRatingInlineFormSet(data=request.POST, instance=party)

            if party_rating_formset.is_valid():
                for form in party_rating_formset:
                    form.save()
                    return HttpResponseRedirect(reverse(get_update_url(Party), args=[party.id]))

    else:
        context = { 'party_form': party_form, 'party_rating_formset': party_rating_formset,}
        context = get_update_context_additions(context, Party, party)
        return render(request, 'core/party_form.html', context)

Forms

class PartyForm(forms.ModelForm):
    class Meta:
        model = Party
        fields = "__all__"
        exclude = get_form_exclude_default()
        widgets = {
            'last_credit_assessment': DateInput(),
        }

class PartyRatingForm(forms.ModelForm):
    class Meta:
        model = PartyRating
        fields = "__all__"
        exclude = get_form_exclude_default()

PartyRatingInlineFormSet = inlineformset_factory(Party, PartyRating, fields=('rating_source','rating','outlook',), form=PartyRatingForm, extra=1, can_delete=True)

Template

<form method="post">{% csrf_token %}
    {{ form.errors }}
    {{ form.non_field_errors }}
    <table>
      {% for field in party_form %}
      <tr><td>{{ field.label_tag }}</td><td>{{ field }}</td></tr>
      {% endfor %}
    </table><br>
    <table>
      <tr>
      {% for field in party_rating_formset.form %}
      <th>{{ field.label_tag }}</th>
      {% endfor %}
      </tr>
      {% for form in party_rating_formset %}
      <tr>
      {% for field in form %}
      <td>{{ field }}</td>
      {% endfor %}
      </tr>
      {% endfor %}
    </table><br>
    <input type="submit" value="Submit">
</form>

Welcome @travisaw !

You appear to have two identical forms in your template. Is that intentional or simply a mistake in your post?

Hi @KenWhitesell ,

Thanks for the quick response. Yes, this duplicate form in the template was merely a mistake. I have now removed it from the original post.

I do not see where you’re rendering the management_form for the formset.

See Using the formset in the template.

Brilliant. Thanks for pointing this out @KenWhitesell. I had seen the formset management section in the documentation but didn’t grasp it’s importance the first time.

Updated the template code as such and the input is saving now.

<form method="post">{% csrf_token %}
    {{ form.errors }}
    {{ form.non_field_errors }}
    <table>
      {% for field in party_form %}
      <tr><td>{{ field.label_tag }}</td><td>{{ field }}</td></tr>
      {% endfor %}
    </table><br>
    <table>
      <tr>
      {{ party_rating_formset.management_form }}
      {% for field in party_rating_formset.form %}
      <th>{{ field.label_tag }}</th>
      {% endfor %}
      </tr>
      {% for form in party_rating_formset %}
      <tr>
      {% for field in form %}
      <td>{{ field }}</td>
      {% endfor %}
      </tr>
      {% endfor %}
    </table><br>
    <input type="submit" value="Submit">
</form>