Form with ModelChoiceField(required=True) doesn't raise ValidationError if no option is selected

Viewed 192

I have a form that raises ValidationErrors for all fields if they are not valid. However, if there is no option from the ModelChoiceField selected, the form both doesn't submit (which is fine) and doesn't raise any ValidationError in the template either.

It seems that if request.method == 'POST': returns False if no category is selected on submit.

# Form

class PollersForm(forms.Form):

    # Poller Category
    poller_category = forms.ModelChoiceField(widget=forms.RadioSelect(attrs={
        'class': 'poller-category-radio',
    }), queryset=Category.objects.all().order_by('category'), label='Select a Category', required=True)
# View

def raise_poller(request):
    """
    creates a new Poller object according to user inputs and redirects to it
    """
    # if this is a POST request we need to process the form data
    if request.method == 'POST':

        # create a form instance and populate it with data from the request:
        form = PollersForm(request.POST)

        # check whether it's valid:
        if form.is_valid():
            # process the remaining data in form.cleaned_data as required
            poller_category = form.cleaned_data['poller_category']

            # Get the user
            created_by = request.user

            # Save the poller to the database
            p = Poller(
                       poller_category=poller_category,
            )
            p.save()

            # Get the created object and its id to redirect to the single poller view
            created_poller = Poller.objects.get(created_by=created_by, id=p.pk)
            created_poller_id = created_poller.pk
            created_poller_slug = created_poller.slug
            poller_url = '/poller/' + str(created_poller_id) + '/' + str(created_poller_slug)

            # redirect to a new URL:
            return redirect(poller_url, request, created_poller_id)

    # if a GET (or any other method) we'll create a blank form
    else:
        form = PollersForm()

    return render(request, 'pollinator/raise_poller.html', {'form': form})
<!-- Template -->

    <form action="/raisepoller/" method="post">
        {% csrf_token %}
        {{ form.non_field_errors }}
        {{ form.errors }}

        <!-- Poller Categories -->
        <div class="fieldWrapper">
            <div class="label">{{ form.poller_category.label_tag }}</div>
            <div class="category-ctn">
                {% for category in form.poller_category %}
                    <div class="category-wrapper">{{ category }}</div>
                {% endfor %}
            </div>
        </div>

        <!-- Submit Button -->
        <button type="submit" value="Publish Poller" id="raise-poller-button">
            <div class="button-wrapper">
                <div class="button-icon"><img id="button-image" src="{% static 'images/send.png' %}"></div>
                <div class="button-text">Publish Poller</div>
            </div>
        </button>
    </form>

...which renders:

<div class="category-wrapper"><label for="id_poller_category_1"><input type="radio" name="poller_category" value="5" class="poller-category-radio" id="id_poller_category_1" required="">
 Environment</label></div>
/* CSS */

.category-wrapper input {
    position: fixed;
    visibility: hidden;
}

.category-wrapper label {
    display: inline-block;
    background-color: #fdfdfd;
    color: var(--main-darkblue-color);
    padding: 0.2vw 0.5vw;
    font-size: 1vw;
    border: 2px solid #444;
    border-radius: 4px;
    margin-bottom: 0;
}

enter image description here

3 Answers

It seems that if request.method == 'POST': returns False if no category is selected on submit.

The browser doesn't even send a request because the HTML5 validation fails.

The validation message is not shown because of visibility: hidden on input.

.category-wrapper input {
    position: fixed;
    visibility: hidden;
}

...which causes this error shown in your browser console:

❗An invalid form control with name='category' is not focusable.

Instead, make it transparent, width-less and margin-less:

.category-wrapper input {
    margin: 0;
    opacity: 0;
    width: 0;
}

This is happening becouse the view pass the first if

if request.method == 'POST':

create the form

    # create a form instance and populate it with data from the request:
    form = PollersForm(request.POST)

then check if it is valid

    # check whether it's valid:
    if form.is_valid():

since it is not valid the view goes back to the first indentation and rendering the template with the form with the error inside.

else:
    form = PollersForm()

return render(request, 'pollinator/raise_poller.html', {'form': form})

the best thing you can do is to take the clean form of the request.method out of the else

form = PollersForm()

return render(request, 'pollinator/raise_poller.html', {'form': form})

required = true
only checks if there is something in the field
by this
can you check in response data that there is any default value for that field?

Related