javascripthtmlformsvalidationflask-wtforms

How to generate HTML5 nice validation error messages from Flask WTForms Validation Error


I have attempted to use Flask WTForms and put together some HTML code to generate a submission form on a website. The following is the involved code so far:

server.py:

from flask import Flask, render_template, request, url_for, redirect, flash, send_from_directory, jsonify
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, SelectField, DateField, PasswordField, SubmitField, EmailField
from wtforms.validators import DataRequired, ValidationError, Email, NumberRange
from datetime import datetime, timedelta

class TestForm(FlaskForm):
    name = StringField(label='Name', validators=[DataRequired("You must include a name")])
    start_search = DateField(
        label='Start date', validators=[DataRequired()], default=datetime.now() + timedelta(days=1))
    end_search = DateField(
        label='End date', validators=[DataRequired()], default=datetime.now() + timedelta(days=2))
    submit = SubmitField(label="Submit")

app = Flask(__name__)

@app.route('/test', methods=['GET', 'POST'])
def test():
    form = TestForm()
    if form.validate_on_submit():
        return redirect('/success')
    return render_template('test.html', title='Test', form=form)

test.html:

<form class="form" method="POST" id="form" style="display: block;">
{{ form.csrf_token() }}
<div class="container">
    <div class="row">
    {{ form.name.label }}
    {{ form.name(class="form-control") }}
    </div>

    <div class="row">
    {{ form.start_search.label }}
    {{ form.start_search(class="form-control") }}
    </div>

    <div class="row">
    {{ form.end_search.label }}
    {{ form.end_search(class="form-control") }}
    </div>

    <div class="row">
        {{ form.submit(class="form-control") }}
    </div>
</div>
</form>

Website:

enter image description here

And everything works fine. If I try to submit when the name field is empty, since it has the DataRequired validator, I get this:

enter image description here

And that's wonderful, but I want to do some custom validation on the fields, for instance, returning the custom "You must include a name" error that I have added to the DataRequired for the name field, as opposed to the default that I seem to be getting. I have seen online that adding the novalidate tag on the html form definition allows one to be able to write custom errors next to the field, like so:

<form class="form" method="POST" id="form" style="display: block;" novalidate>
{{ form.csrf_token() }}
<div class="container">
    <div class="row">
    {{ form.name.label }}
    {{ form.name(class="form-control") }}
    {% for error in form.name.errors %}
    <span style="color: red;">[{{ error }}]</span>
    {% endfor %}
    </div>

    <div class="row">
    {{ form.start_search.label }}
    {{ form.start_search(class="form-control") }}
    </div>

    <div class="row">
    {{ form.end_search.label }}
    {{ form.end_search(class="form-control") }}
    </div>

    <div class="row">
        {{ form.submit(class="form-control") }}
    </div>
</div>
</form>

enter image description here

This is great, but it is not what I want. I want to generate the same nice pop-up that I was seeing before, simply with a custom error message. Moreover, when I add the novalidate tag, reloading the website yields a "Confirm Form Resubmission" pop-up, which didn't happen before, and I don't understand why: enter image description here

The following questions are similar, yet not fully answered: Flask form validation error prompt message https://www.reddit.com/r/flask/comments/lkg4w6/understanding_the_flask_wtforms_validation_popup/

Thank you for your help in advance.

I am using


Solution

  • The topic of form validation is kind of a meta-issue. You can break it down into two pieces:

    1. whether to validate the form with javascript in the browser before you submit.
    2. how to use wtforms to add custom validation on the server side validation. I don't think there is a simple answer to whether to validate on the client side vs the server side. It's possible to make a much more user-friendly validation UI in javascript, but it doesn't completely eliminate the need to validate on the server side. I tend to use a combination of both.

    WTForms doesn't help with validation in the browser, and for that you would need to write some javascript to react to form events in the browser. Browsers have only minimal validation built-in, such as when you specify required for a field in wtforms, this causes the browser to generate the required attribute on an input, and the browser will supply generic hints to the user to fill out the field before it submits it. These generic hints are like the "Please fill out this field" popup you saw. Once the browser actually submits the field, the validators of WTForms actually run on the server side, and you can run arbitrary python code there to enforce further restrictions. Error messages in python can show up as form.errors or field.errors in the jinja template you use to render your form. There is an (incomplete) example in the WTForms documentation.

    The resubmission warning is merely to enforce the semantics of a POST, which was traditionally used to cause a change of state on the server side. This will happen if you hit the back button after submitting. What you want to do when a form is submitted with a POST is to re-render the form with the error messages in it, so that the user isn't tempted to hit the back button to try again.