In web development, forms are a crucial tool for collecting user information. Whether it’s for login/registration, feedback submission, or data collection, forms are an indispensable component. As a lightweight web framework, Flask can easily implement form creation, validation, and data processing when combined with the Flask-WTF extension. This article will guide you through the core process of handling Flask forms using a complete example.
I. Preparation: Install Required Tools¶
First, ensure Flask and Flask-WTF are installed. If not, you can install them via pip:
pip install flask flask-wtf
II. Step 1: Create a Form Class¶
The core of a form lies in defining the fields to collect and validation rules. By inheriting the FlaskForm class, you can utilize the field types (e.g., StringField, PasswordField) and built-in validators (e.g., DataRequired, Email) provided by Flask-WTF.
Example: Create a User Login Form
# forms.py file
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, ValidationError
class LoginForm(FlaskForm):
# Username: required, length 4-20 characters
username = StringField('Username', validators=[
DataRequired(message='Username cannot be empty'),
Length(min=4, max=20, message='Username must be between 4-20 characters')
])
# Email: required, format validation
email = StringField('Email', validators=[
DataRequired(message='Email cannot be empty'),
Email(message='Please enter a valid email address')
])
# Password: required, minimum 8 characters
password = PasswordField('Password', validators=[
DataRequired(message='Password cannot be empty'),
Length(min=8, message='Password must be at least 8 characters')
])
# Submit button
submit = SubmitField('Login')
III. Step 2: Write Flask Application and View Functions¶
In a Flask application, we need to handle two types of requests:
- GET Request: Render the form for user input
- POST Request: Receive form data, validate, and process it
Example: Main Application Code
# app.py file
from flask import Flask, render_template, redirect, url_for, flash
from forms import LoginForm # Import the form class
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here' # Required for CSRF protection (use a random string in production)
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm() # Instantiate the form class
if form.validate_on_submit(): # Check if it's a POST request and data validation passes
# Get form data
username = form.username.data
email = form.email.data
password = form.password.data # Encrypt passwords in production (for demonstration only)
# Simulate data processing (e.g., validate user credentials, redirect after login)
flash(f'Login successful! Welcome back, {username}') # Show success message
return redirect(url_for('dashboard')) # Redirect to dashboard page
return render_template('login.html', form=form) # Render the form page
@app.route('/dashboard')
def dashboard():
return render_template('dashboard.html') # Data display page
if __name__ == '__main__':
app.run(debug=True)
IV. Step 3: Design HTML Templates (Form Rendering and Data Display)¶
Templates are responsible for rendering forms on the frontend and displaying processing results. Use the form object’s methods to render form fields and include form.hidden_tag() to add the CSRF token (built-in protection by Flask-WTF).
1. Login Form Template (login.html)¶
<!-- templates/login.html -->
<!DOCTYPE html>
<html>
<head>
<title>User Login</title>
<style>
.error { color: red; }
.form-group { margin-bottom: 15px; }
</style>
</head>
<body>
<h1>User Login</h1>
<!-- Display flash messages -->
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for msg in messages %}
<div style="color: green;">{{ msg }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<!-- Render the form -->
<form method="POST">
{{ form.hidden_tag() }} <!-- Required for CSRF protection -->
<!-- Username -->
<div class="form-group">
{{ form.username.label }}<br>
{{ form.username(size=32, class="form-control") }} <!-- Render input field -->
{% for error in form.username.errors %} <!-- Display validation errors -->
<span class="error">{{ error }}</span>
{% endfor %}
</div>
<!-- Email -->
<div class="form-group">
{{ form.email.label }}<br>
{{ form.email(size=32, class="form-control") }}
{% for error in form.email.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
</div>
<!-- Password -->
<div class="form-group">
{{ form.password.label }}<br>
{{ form.password(size=32, class="form-control") }}
{% for error in form.password.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
</div>
<!-- Submit button -->
{{ form.submit(class="btn btn-primary") }}
</form>
</body>
</html>
2. Data Display Template (dashboard.html)¶
<!-- templates/dashboard.html -->
<!DOCTYPE html>
<html>
<head>
<title>Login Successful</title>
</head>
<body>
<h1>Welcome to Dashboard</h1>
<p>Here you can view user data, for example:</p>
<ul>
<li>Username: {{ session.get('username') }}</li> <!-- Assume username is stored in session -->
<li>Email: {{ session.get('email') }}</li>
</ul>
</body>
</html>
V. Key Details Explained¶
1. CSRF Protection¶
Flask-WTF enables CSRF protection by default, automatically generating a CSRF token via form.hidden_tag(). This tag must be included in the template; otherwise, form submissions will fail.
2. Data Validation¶
form.validate_on_submit() automatically executes all validation rules. If validation fails, error messages are stored in form.<field_name>.errors and displayed in the template via loops.
3. Avoid Duplicate Submissions¶
Use redirects (e.g., redirect(url_for('dashboard'))) instead of directly returning templates to prevent duplicate submissions when users refresh the page.
4. Data Storage¶
In the example, form.<field>.data is retrieved directly. In production, data should be saved to a database (e.g., with SQLAlchemy) and passwords should be encrypted (e.g., using bcrypt).
VI. Complete Process Summary¶
- User Fills Form: Data is entered via the form rendered in
login.html - Frontend Validation: The browser automatically checks required fields and formats (e.g., email format)
- Backend Validation: Flask-WTF validates data integrity (e.g., length, format)
- Data Processing: After validation, data is retrieved and processed (e.g., login authentication)
- Result Display: Processed data is shown via
dashboard.html
VII. Advanced Tips¶
- Custom Validators: For complex validation (e.g., checking if a username exists), define methods in the form class:
def validate_username(self, field):
if User.query.filter_by(username=field.data).first():
raise ValidationError('Username is already registered')
- Multi-Form Handling: Use
FlaskFormto create multiple form types and merge data withform = MultiForm(request.form) - File Uploads: Combine
FileFieldwithFileAllowedvalidators for image/file uploads
By following these steps, you’ve mastered the core process of handling Flask forms. From form definition to data display, each step ensures user experience and data security. Extend functionality in practice as needed, such as adding database interactions or permission controls.