WTForms: Disable client-side validation on cancel

13.7k views Asked by At

What I'm asking for is actually quite simple. I want to create a form with some fields and a submit and a cancel button. I want to use the quick_form template function of Flask-Bootstrap to keep overhead in my template low. My form looks like this:

from flask_wtf import FlaskForm
from wtforms.validators import Required, Length


class SequenceForm(FlaskForm):
    name = StringField('Name:', validators=[Required(), Length(1, 128)])

    # some other fields here

    submit = SubmitField('submit')
    cancel = SubmitField('cancel')

The template:

{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}

{% block content %}
  <div class="container">
    <form method="POST">
      <div class="row">
        <div class="col-xs-12">
          {{ wtf.quick_form(form, button_map={'submit': 'primary'}) }}
        </div>
      </div>
    </form>
  </div>
{% endblock %}

As one would suspect I want to validate and accept the input values on submit and skip validation on cancel. So my view function looks as expected.

@main.route('sequence/', methods=['GET', 'POST'])
def sequence():
    form = SequenceForm()
    if request.method == 'POST':
        if 'submit' in request.form:
            if form.validate_on_submit():
                print(form.duration.data)
        else:
            return redirect(url_for('main.index'))
    return render_template('sequence.html', form=form)

Now if cancel is pressed there should logically be no validation and the redirect should take place. However I run into the issue that my view function doesn't even get called if I press either submit or cancel due to the client-side validation.

<input class="form-control" id="name" name="name" required="" type="text" value="">

Is there a way to disable client-side validation on WTForms?

4

There are 4 answers

4
MrLeeh On BEST ANSWER

The Required validator as well as the DataRequired and InputRequired which replace Required since version 3 of WTForms set the replace flag of the field. This flag is used to add the required attribute to the HTML representation of the field. My workaround is to manually create a validate function.

from wtforms.validators import ValidationError

def _required(form, field):
    if not field.raw_data or not field.raw_data[0]:
        raise ValidationError('Field is required')

class SequenceForm(FlaskForm):
    name = StringField('Name:', validators=[_required, Length(1, 128)])

    # some other fields here

    submit = SubmitField('submit')
    cancel = SubmitField('cancel')

This way there is no validation on the client-side and it is ensured that the view function is called on every submit or cancel.

Note

An even simpler solution is to subclass the InputRequired validator and overwrite the field_flags dictionary.

from wtforms.validators import InputRequired

class MyInputRequired(InputRequired):
    field_flags = ()

class SequenceForm(FlaskForm):
    name = StringField('Name:', validators=[MyInputRequired(), Length(1, 128)])
2
VPfB On

You could forbid rendering of the required attr.

class MyTextInput(wtforms.widgets.TextInput):
    def __call__(self, field, **kwargs):
        kwargs['required'] = False
        return super().__call__(field, **kwargs)

For Python2 add args like this:super(MyTextInput, self)

and then:

name = StringField('Name:', validators=[Required(), Length(1, 128)], widget=MyTextInput())
0
LuckyDams On

To disable client-side form validation, add the 'novalidate' attribute to the HTML <form> element in your template:

<form method="POST" novalidate>

0
Grey Li On

Since you use Flask-Bootstrap's quick_form() macro, you can just set novalidate parameter to True to disable client-side validation (it will set a novalidate attribute to your HTML <form> element):

{{ wtf.quick_form(form, novalidate=True) }}

If you are using Bootstrap-Flask, the method is similar:

{{ render_form(form, novalidate=True) }}