In web applications, forms are one of the core ways users interact with the server—scenarios like login, registration, and feedback submission all require forms. Flask itself doesn’t directly provide form handling capabilities, but through the extension library Flask-WTF (based on WTForms), you can easily implement form creation, data validation, and secure processing. This article will take you through the core knowledge of form handling and validation in Flask, starting from scratch.
1. Environment Setup: Install Required Libraries¶
First, ensure Flask and Flask-WTF are installed. Open your terminal and run the following command:
pip install flask flask-wtf
Flask: The foundation of the web framework.Flask-WTF: A Flask extension for forms, which includes WTForms (a powerful form processing library) and CSRF protection (to prevent cross-site request forgery attacks).
2. Core Concepts: Form Classes and Validators¶
We define the form structure and validation rules using a form class. Each field (e.g., username, password) corresponds to a class attribute, and each attribute can have multiple validators (e.g., non-empty, email format, length limits).
1. Defining the Form Class¶
Using a “user registration” form as an example, create a RegistrationForm class that inherits from FlaskForm (the base class for Flask-WTF). Each field is defined using types like StringField or PasswordField, with validation rules added via the validators parameter.
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length, EqualTo
class RegistrationForm(FlaskForm):
# Username: non-empty, length 4-20
username = StringField('Username',
validators=[DataRequired(), Length(min=4, max=20)])
# Email: non-empty, valid email format
email = StringField('Email',
validators=[DataRequired(), Email()])
# Password: non-empty, length 8-20
password = PasswordField('Password',
validators=[DataRequired(), Length(min=8, max=20)])
# Confirm Password: must match password, non-empty
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password',
message='Passwords do not match')])
# Submit button
submit = SubmitField('Register')
Key Explanations:
- StringField: Regular text input (e.g., username, email).
- PasswordField: Password input (automatically hides input content).
- SubmitField: Submit button.
- validators: List of validators, each responsible for checking field legitimacy:
- DataRequired(): Field cannot be empty.
- Email(): Validates email format.
- Length(min, max): Limits string length.
- EqualTo('field'): Ensures the value matches the specified field (e.g., confirm password).
3. Rendering Forms in Flask¶
Once the form class is defined, instantiate the form in a view function and render it in a template.
1. View Function to Handle Forms¶
In app.py, define a view function to handle GET/POST requests:
from flask import Flask, render_template, redirect, url_for, flash
from forms import RegistrationForm # Import the form class
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here' # Required for CSRF protection (use a random string)
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm() # Instantiate the form class
if form.validate_on_submit(): # Check if it's a POST request and data is valid
# Data is valid: retrieve form data (e.g., form.username.data)
username = form.username.data
email = form.email.data
password = form.password.data
# Add subsequent operations here (e.g., save to database)
flash('Registration successful!') # Display a success message (to be shown in the template)
return redirect(url_for('index')) # Redirect to the homepage
return render_template('register.html', form=form) # Render the template with the form
if __name__ == '__main__':
app.run(debug=True)
2. Template to Render the Form (register.html)¶
Use Jinja2 template syntax to render the form, ensuring CSRF token generation:
<!DOCTYPE html>
<html>
<head>
<title>Register</title>
</head>
<body>
<h1>User Registration</h1>
<!-- Display flash messages -->
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div style="color: green;">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<!-- Form rendering -->
<form method="POST">
{{ form.hidden_tag() }} <!-- Required for CSRF protection -->
<!-- Username -->
<div>
{{ form.username.label }}
{{ form.username(size=32) }}
<!-- Display validation errors -->
{% for error in form.username.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<!-- Email -->
<div>
{{ form.email.label }}
{{ form.email(size=64) }}
{% for error in form.email.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<!-- Password -->
<div>
{{ form.password.label }}
{{ form.password(size=32) }}
{% for error in form.password.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<!-- Confirm Password -->
<div>
{{ form.confirm_password.label }}
{{ form.confirm_password(size=32) }}
{% for error in form.confirm_password.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<!-- Submit Button -->
<div>{{ form.submit() }}</div>
</form>
</body>
</html>
4. Handling Form Submission and Validation¶
When a user submits a form, Flask-WTF automatically performs the following steps:
1. Check if the request method is POST.
2. Validate the CSRF token (to prevent forged requests).
3. Run all validators to check data legitimacy.
4. If all validations pass, form.validate_on_submit() returns True, and data can be processed.
Key Workflow¶
- GET Request: Render an empty form (user’s first visit).
- POST Request:
- If valid: Retrieve data (
form.xxx.data) and perform actions (e.g., save to database). - If invalid: Display error messages in the template (via
form.xxx.errors).
5. Complete Example: From Form to Database¶
To store user data in a database, combine with SQLAlchemy (Flask’s ORM extension). Here’s a simplified example (assuming flask-sqlalchemy is installed):
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(20), nullable=False)
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# Check if username/email already exists (optional)
existing_user = User.query.filter_by(username=form.username.data).first()
if existing_user:
flash('Username already exists!')
return redirect(url_for('register'))
# Create and save new user
new_user = User(
username=form.username.data,
email=form.email.data,
password=form.password.data # In production, hash the password!
)
db.session.add(new_user)
db.session.commit()
flash('Registration successful!')
return redirect(url_for('index'))
return render_template('register.html', form=form)
6. Summary¶
The core steps for Flask form handling are:
1. Define the Form Class: Use FlaskForm and WTForms fields with validators.
2. View Function Handling: Instantiate the form, check requests, and validate data.
3. Template Rendering: Use form.hidden_tag() for CSRF protection, and display fields/errors.
4. Data Processing: Retrieve validated data and perform subsequent operations (e.g., database storage).
With WTForms validators, you can easily enforce checks like non-empty, format, and length constraints. Combined with Flask-WTF’s CSRF protection, you can quickly build secure and reliable form systems.
Tip: Beginners should start with simple scenarios (e.g., login forms) and gradually master complex validations (e.g., file uploads, multi-field relationships). The official Flask-WTF (https://flask-wtf.readthedocs.io/) and WTForms (https://wtforms.readthedocs.io/) documentation are excellent resources for advanced learning.