5

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?

MrLeeh
  • 5,321
  • 6
  • 33
  • 51

4 Answers4

9

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) }}
Grey Li
  • 11,664
  • 4
  • 54
  • 64
3

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)])
MrLeeh
  • 5,321
  • 6
  • 33
  • 51
  • You could simply reset the tuple of flags to be set: `class MyInputRequired(wtforms.validators.InputRequired):` next line: `field_flags = ()` (can't get the formatting right....) – VPfB Dec 25 '16 at 10:58
  • @VPfB That is a great solution. If you want to create an answer for it I would accept it. – MrLeeh Dec 25 '16 at 11:44
  • I think it is very similar to your solution. And you came first with the idea. – VPfB Dec 25 '16 at 12:42
  • Actually it isn' t because validation is transfered from client-side to the view funciton. This way one can skip validation for "cancel". – MrLeeh Jan 13 '20 at 06:36
1

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())
VPfB
  • 14,927
  • 6
  • 41
  • 75
  • This is a good solution for a specific field type. However if I got multiple field types in my form I need to create a separate widget for each of them. – MrLeeh Dec 25 '16 at 10:16
  • @MrLeeh Yes, that's true. You just have to modify one of the items in the chain (validator, flags, widget, quick_form) – VPfB Dec 25 '16 at 11:05
1

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

<form method="POST" novalidate>

LuckyDams
  • 11
  • 1