Skip to content
Advertisement

How to fix Django Duplicate key form from being valid and saved?

This is my views.py file and Im getting this message

DETAIL:  Key (member_id, match_id)=(4, 20) already exists.

However is see that Valid is printing so its obvious the is_valid() method doesnt catch this duplicate key.

def update_batter_form(request, match_id, batting_id):
    """Edit existing batter form view."""

    obj = Batting.objects.get(pk=batting_id)
    form = BattingForm(instance=obj)
    batting_list_bn = Batting.objects.order_by('batter_number') 
        .filter(match_id=match_id)
    match = Match.objects.get(pk=match_id)
    if request.method == 'POST':
        print("Printing POST")
        form = BattingForm(request.POST, instance=obj)
        print(obj)
        if form.is_valid():
            print("Valid")
            form.save()
            return redirect('/club/match/'+match_id+'/batter/'+batting_id)
        else:
            print(form.errors)
            return redirect('/club/match/' + match_id + '/batter/' + batting_id)

    context = {
        'form': form,
        'batting_list_bn': batting_list_bn,
        'match': match
    }

    return render(request, 'club/batter_update.html', context)

EDIT Batting model and form

class Batting(models.Model):
    """Batting performances within a match."""

    member = models.ForeignKey(Member, on_delete=models.CASCADE, default='')
    batter_number = models.IntegerField(
        blank=False, default=1, validators=[MaxValueValidator(11), MinValueValidator(1)]
    )
    fours = models.IntegerField(
        blank=True, null=True, default=0, validators=[MaxValueValidator(300), MinValueValidator(0)]
    )
    sixes = models.IntegerField(
        blank=True, null=True, default=0, validators=[MaxValueValidator(300), MinValueValidator(0)]
    )
    runs = models.IntegerField(
        blank=True, null=True, default=0, validators=[MaxValueValidator(1000), MinValueValidator(0)]
    )
    mode_of_dismissal = models.ForeignKey(Wicket, on_delete=models.CASCADE, default='')
    out_by = models.ForeignKey(
        OppositionNames, on_delete=models.CASCADE, default='', blank=True, null=True
    )
    match = models.ForeignKey(Match, on_delete=models.CASCADE, default='')

    class Meta:
        """Meta class."""

        unique_together = (("batter_number", "match",), ("member", "match",),)

    def __str__(self):
        return str('{0} {1} scored {2} runs'
                   .format(self.member, self.match.date, self.runs)
                   )
class BattingForm(forms.ModelForm):

    class Meta:
        model = Batting
        exclude = ('match',)

How is it I catch this issue?

Advertisement

Answer

In your form you have set exclude as follows: exclude = ('match',). When we exclude a field from the form, the form also excludes it from the validation it performs, which is only logical in the case that the form may be used to create this model’s instances.

Since you need the field match to be part of the validation the best method is to simply make match a disabled and hidden field on the form. This will cause the field to be rendered with the disabled and hidden attributes. Also even if the user tampers with the field it will be ignored in favor of the initial value of the field:

class BattingForm(forms.ModelForm):
    match = forms.ModelChoiceField(
        queryset=Match.objects.all(),
        widget=forms.HiddenInput(),
        disabled=True
    )
    
    class Meta:
        model = Batting
        fields = '__all__'
Advertisement