Form ValidationError Not Showing in Form

I’m creating a form to add a curling shot to the database. I’m validating if the game, player position, shot number and end are the same, then raise a validation error in the form. When I do this, the validation error isn’t showing in the HTML of the form. How can I have the validation error show in the form HTML?

form.py:

class ShotAddForm(forms.ModelForm):
    required_css_class = 'required'
    # Give an error if the game, player position, shot number and end already have a shot in the database.
    def clean(self):
        cleaned_data = super().clean()
        game = cleaned_data.get('game')
        player_position = cleaned_data.get('player_position')
        shot_number = cleaned_data.get('shot_number')
        end = cleaned_data.get('end')
        if CurlingShot.objects.filter(game = game).filter(player_position = player_position).filter(shot_number = shot_number).filter(end = end).exists():
            logger.debug("Duplicate Shot")
            #FIX: Error message isn't showing in the form if there is a duplicate. Current behavior is the form reloads with blank fields.
            raise forms.ValidationError("There is already a shot for this player position, shot number and end.", code='duplicate_shot')
    class Meta:
        model = CurlingShot
        fields = ('game','player_name','player_position', 'shot_number', 'end','shot_type','curl','rock_position','hog_to_hog_time','split_time','make_shot')

views.py:

@login_required(login_url='/login/')
def shot_add(request, game_pk=''):
	if request.method == 'POST':
		form = ShotAddForm(request.POST)
		if form.is_valid():
			shot = form.save(commit=False)
			shot.current_owner = request.user
			shot.save()
			# Save and new form button to render the form again otherwise return to the game listing.
			if 'save_and_new' in request.POST:
				return render(request, 'curlingAnalytics/shot_add.html',{'form':ShotAddForm(initial={'game':shot.game})})
			else:
				return redirect(request.POST.get('next'))
		else:
			logger.debug("Shot Add Form Not Valid")
			return render(request, 'curlingAnalytics/shot_add.html',{'form':ShotAddForm(initial={'game':game_pk})})
	else:
		form = ShotAddForm(initial={'game':game_pk})
	return render(request, 'curlingAnalytics/shot_add.html',{'form':form})

shot_add.html:

{% extends 'curling/base.html' %}

{% block title %}Add a New Game Shot{% endblock %}

{% block content %}
	{% if user.is_authenticated %}
    <form action="" method="POST" class="club-form">{% csrf_token %}
        {{ form.non_field_errors }}
        {{ form.as_p }}
        <input type="hidden" name="next" value="{{ request.META.HTTP_REFERER }}">
        <button type="submit" class="save btn btn-secondary">Save Shot</button><button type="submit" name="save_and_new" class="save btn btn-secondary">Save & New Shot</button>
    </form>
	{% endif %}
{% endblock %}

Hello,

Form errors: The Forms API | Django documentation | Django

Instead of creating a new form instance in your view when the form is invalid, you should pass the existing form instance (which contains the validation errors) back to the template.

Here’s how you can adjust your views.py to ensure that the form with validation errors is correctly passed back to the template:

@login_required(login_url='/login/')
def shot_add(request, game_pk=''):
    if request.method == 'POST':
        form = ShotAddForm(request.POST)
        if form.is_valid():
            shot = form.save(commit=False)
            shot.current_owner = request.user
            shot.save()
            # Save and new form button to render the form again otherwise return to the game listing.
            if 'save_and_new' in request.POST:
                return render(request, 'curlingAnalytics/shot_add.html', {'form': ShotAddForm(initial={'game': shot.game})})
            else:
                return redirect(request.POST.get('next'))
        else:
            logger.debug("Shot Add Form Not Valid")
            return render(request, 'curlingAnalytics/shot_add.html', {'form': form})  # Pass the form with errors
    else:
        form = ShotAddForm(initial={'game': game_pk})
    return render(request, 'curlingAnalytics/shot_add.html', {'form': form})

Also, ensure that your template (shot_add.html ) is correctly displaying the non-field errors by including {{ form.non_field_errors }} in the appropriate place:

{% extends 'curling/base.html' %}

{% block title %}Add a New Game Shot{% endblock %}

{% block content %}
    {% if user.is_authenticated %}
    <form action="" method="POST" class="club-form">{% csrf_token %}
        {{ form.non_field_errors }}
        {{ form.as_p }}
        <input type="hidden" name="next" value="{{ request.META.HTTP_REFERER }}">
        <button type="submit" class="save btn btn-secondary">Save Shot</button>
        <button type="submit" name="save_and_new" class="save btn btn-secondary">Save & New Shot</button>
    </form>
    {% endif %}
{% endblock %}

Thanks! That worked like a charm.

1 Like