Form Validation With Flask-Meld

Posted on Fri 18 December 2020 in flask-meld

Real-time form validation with Flask-Meld  

Flask-Meld now integrates with Flask-WTF to handle form validation.

What's the big deal? Real-time form validation without writing any Javascript!

Flask-Meld form validation

Of course I wouldn't leave you hanging without a way to try it yourself! Here's a link to the demo

Building your form

While you can use WTForms on it's own, Flask-WTF adds CSRF protection automatically. That alone makes Flask-WTF worth using.

Define your form with Flask-WTF just as you always do.

# forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, EqualTo


class RegistrationForm(FlaskForm):
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    password_confirm = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Submit')

Create your template

Use WTForm helpers to create your form in your HTML template.

<!-- templates/meld/register.html -->
<div>
    <form method="POST">
        <div>
            {{ form.email.label }}
            {{ form.email }}
            <span> {{ errors.password | first }} </span>
        </div>

        <div>
            {{ form.password.label }}
            {{ form.password }}
            <span> {{ errors.password | first }} </span>
        </div>
        <div>
            {{ form.password_confirm.label }}
            {{ form.password_confirm }}
            <span> {{ errors.password_confirm | first }} </span>
        </div>
        <div>
            {{ form.submit }}
        </div>
    </form>
</div>

Using the WTForm helpers saves you some typing. Alternatively, you can define your HTML form without using the helpers. For example, to make a field use <input id="email" meld:model="email" name="email" required="" type="text" value=""> Make sure that meld:model="name_of_field" exists on each field.

Define the form in the component

Flask-Meld requires you to define a form on your component. This tells the component which form to load and binds all of the form fields to the component.

# meld/components/register.py
from flask_meld import Component
from forms import RegistrationForm


class Register(Component):
    form = RegistrationForm()

Realtime form validation

To make your form validate as a user types use the updated function. This will provide the form field and allow you to validate on the fly. Simply call validate with the field as an argument. Note: If you call self.validate() without a field argument it will validate the entire form instead of the individual field.

# meld/components/register.py
from flask_meld import Component
from forms import RegistrationForm


class Register(Component):
    form = RegistrationForm()

    def updated(self, field):
        self.validate(field)

Routes

Lastly, let's take a look at the app.py file. This gives a complete example of what it takes to have real-time validation on a form with Flask-Meld. For the most part, everything looks the same as it usually would. There is a small difference, you don't pass the form into the template. The form is defined in the component with form_class.

from flask import Flask, render_template, redirect, url_for, request
from flask_meld import Meld
from forms import RegistrationForm, InventoryForm
from collections import namedtuple

app = Flask(__name__)
app.config['SECRET_KEY'] = 'big!secret'
Meld(app)
socketio = app.socketio


@app.route('/')
def index():
    return render_template("base.html")


@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        return redirect(url_for(request.url))
    return render_template("register_page.html")

if __name__ == '__main__':
    socketio.run(app)

Going further

With that, you have some real-time validation. Let's build on that example and take a look at what we can do if we add strict password requirements.

# form with strict password requirements
class RegistrationForm(FlaskForm):
    email = StringField("Email", validators=[DataRequired(), Email()])
    password = PasswordField(
        "Password",
        validators=[
            DataRequired(),
            Length(min=8, message="Password be at least 8 characters"),
            Regexp("^(?=.*[a-z])", message="Password must have a lowercase character"),
            Regexp("^(?=.*[A-Z])", message="Password must have an uppercase character"),
            Regexp("^(?=.*\\d)", message="Password must contain a number"),
            Regexp(
                "(?=.*[@$!%*#?&])", message="Password must contain a special character"
            ),
        ],
    )
    password_confirm = PasswordField(
        "Confirm Password",
        validators=[
            DataRequired(),
            EqualTo("password", message="Passwords must match"),
        ],
    )
    submit = SubmitField("Submit")

Reach out on Twitter @mikeabrahamsen and let me know what you think. Feedback appreciated!

Check out Flask-Meld on GitHub

Subscribe to stay up to date with Flask-Meld!

Powered by EmailOctopus