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!
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!