pythondjangopostformset

django formset posts an extra (and blank) row


I have a problem with a django formset that I can't really explain to myself. Here is the involved code.

forms.py:

class ScoreForm(forms.ModelForm):
    class Meta:
        model = models.Score
        fields = (
            'period',
            'home_team_score',
            'away_team_score',
        )


ScoreFormSet = forms.inlineformset_factory(
    models.Fixture,
    models.Score,
    form=ScoreForm,
    extra=5
)

models.py:

class Score(models.Model):
    fixture = models.ForeignKey(Fixture, related_name="score")
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="score")
    inserted_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    period = models.IntegerField(blank=True, null=True)
    home_team_score = models.IntegerField(blank=True, null=True)
    away_team_score = models.IntegerField(blank=True, null=True)

    class Meta:
        db_table = 'score'

views.py:

class InsertScoreView(CreateView):
    form_class = forms.ScoreForm
    model = Score
    template_name = 'gamestream/insert_score.html'
    success_url = "/gamestream/score_list/"

    def get_context_data(self, **kwargs):
        fixture = Fixture.objects.get(id=self.kwargs['fixture_id'])
        data = super(InsertScoreView, self).get_context_data(**kwargs)
        if self.request.POST:
            data['scores'] = forms.ScoreFormSet(self.request.POST)
            data['fixture'] = fixture
        else:
            data['scores'] = forms.ScoreFormSet()
            data['fixture'] = fixture
        return data

    def form_valid(self, form, **kwargs):
        user = self.request.user
        fixture = Fixture.objects.get(id=self.kwargs['fixture_id'])
        context = self.get_context_data()
        formset = forms.ScoreFormSet(self.request.POST)
        if formset.is_valid():
            scores = formset.save(commit=False)
            for score in scores:
                score.fixture = fixture
                score.user = user
                score.save()

        return super(InsertScoreView, self).form_valid(form)

and the template:

<div class="container">
  <h3>Please insert scores for the following match:</h3>
  <h4>{{ fixture.starting_time }} -- {{ fixture.league.league_name }} -- {{ fixture.home_team }} vs {{ fixture.away_team }}</h4>

  <form action="." method="POST">
    {% csrf_token %}
    {{ scores.management_form }}
    {% for form in scores %}
      <p>{{ form }}</p>
    {% endfor %}
      <input type="submit" value="Insert score" class="submit" />
  </form>
</div>

I get this behavior: I am able to insert ALL the values that I want. It does not matter if 1 or 5 they get inserted and saved to the db (which is correct, that's what I want to achieve). The problem is that it looks like and extra 100% blank form gets sent through and that does not validate (because the fixture field is required)! I have removed the constraints in the fixture field of the Score model (for testing purposes) and in fact a completely blank line get inserted into the db. This is why I believe a blank form is being sent through.

I can't really explain this behavior. Where does that blank form (or whatever it is) come from? 🤔

Thanks, Vittorio


Solution

  • So after a lot of tries I have changed the type of the view: from CreateView to FormView and it suddenly started to work just fine.

    I can't really provide more info on this as I do not really know what the difference in the code between the 2 classes is.