Django & Flask Topic
Using Flask-WTF for Form Validation
What to learn: Creating form classes with Flask-WTF, adding validators (e.g., DataRequired(), Email()), and rendering forms safely in templates with CSRF protection. Django Connection: This is a direct parallel to Django's Form class. The concepts of defining form fields, validation, and CSRF protection are core to Django.
Introduction to Flask-WTF
Flask-WTF is an extension that integrates WTForms with Flask, providing:
-
CSRF protection
-
Form validation
-
File upload handling
-
Security features
Installation and Setup
bash
pip install flask-wtf
python
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, TextAreaField, SelectField, BooleanField, FileField
from wtforms.validators import DataRequired, Email, Length, EqualTo, ValidationError
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here' # Required for CSRF
Creating Form Classes
This is where Flask starts to look very much like Django!
Basic Registration Form:
python
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length, EqualTo
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[
DataRequired(message='Username is required'),
Length(min=3, max=50, message='Username must be between 3-50 characters')
])
email = StringField('Email', validators=[
DataRequired(message='Email is required'),
Email(message='Please enter a valid email address')
])
password = PasswordField('Password', validators=[
DataRequired(message='Password is required'),
Length(min=8, message='Password must be at least 8 characters'),
EqualTo('confirm_password', message='Passwords must match')
])
confirm_password = PasswordField('Confirm Password')
submit = SubmitField('Register')
More Complex Form Example:
python
class UserProfileForm(FlaskForm):
first_name = StringField('First Name', validators=[DataRequired()])
last_name = StringField('Last Name', validators=[DataRequired()])
bio = TextAreaField('Biography', validators=[Length(max=500)])
country = SelectField('Country', choices=[
('', 'Select Country'),
('us', 'United States'),
('uk', 'United Kingdom'),
('ca', 'Canada')
])
newsletter = BooleanField('Subscribe to newsletter')
avatar = FileField('Profile Picture')
submit = SubmitField('Update Profile')
Custom Validators
python
def validate_username(form, field):
"""Custom validator to check if username contains only letters and numbers"""
if not field.data.isalnum():
raise ValidationError('Username can only contain letters and numbers')
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[
DataRequired(),
Length(min=3, max=50),
validate_username # Custom validator
])
# ... other fields
Using Forms in Views
python
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit(): # Checks if it's a POST request and validates
# Form is valid - process the data
username = form.username.data
email = form.email.data
password = form.password.data
# In a real app, you'd save to database here
print(f"Creating user: {username}, {email}")
return f'Registration successful for {username}!'
# If GET request or validation failed, show the form
return render_template('register.html', form=form)
Rendering Forms in Templates with CSRF Protection
Template (register.html):
html
<!DOCTYPE html>
<html>
<head>
<title>Register</title>
</head>
<body>
<h2>Register</h2>
<form method="POST">
{{ form.hidden_tag() }} <!-- CSRF Token -->
<div>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">{{ error }}</span><br>
{% endfor %}
</div>
<div>
{{ form.email.label }}<br>
{{ form.email(size=32) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">{{ error }}</span><br>
{% endfor %}
</div>
<div>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">{{ error }}</span><br>
{% endfor %}
</div>
<div>
{{ form.confirm_password.label }}<br>
{{ form.confirm_password(size=32) }}<br>
{% for error in form.confirm_password.errors %}
<span style="color: red;">{{ error }}</span><br>
{% endfor %}
</div>
<div>{{ form.submit() }}</div>
</form>
</body>
</html>
Quick Form Rendering
You can also render forms more quickly:
html
<!-- Quick rendering -->
<form method="POST">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username() }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit() }}</p>
</form>
<!-- Or even quicker -->
<form method="POST">
{{ form.hidden_tag() }}
{% for field in form if field.type != 'HiddenField' and field.type != 'CSRFTokenField' %}
<p>
{{ field.label }}<br>
{{ field() }}<br>
{% for error in field.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
{% endfor %}
<p>{{ form.submit() }}</p>
</form>
Handling File Uploads with Flask-WTF
python
from flask_wtf.file import FileField, FileAllowed, FileRequired
class UploadForm(FlaskForm):
document = FileField('Document', validators=[
FileRequired(),
FileAllowed(['pdf', 'doc', 'docx', 'txt'], 'Document files only!')
])
submit = SubmitField('Upload')
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit():
file = form.document.data
filename = secure_filename(file.filename)
file.save(os.path.join('uploads', filename))
return f'File {filename} uploaded successfully!'
return render_template('upload.html', form=form)
Pre-populating Forms
python
@app.route('/profile/edit', methods=['GET', 'POST'])
def edit_profile():
form = UserProfileForm()
# Pre-populate form with existing data (from database)
if request.method == 'GET':
# In real app, you'd get this from your database
user_data = {
'first_name': 'John',
'last_name': 'Doe',
'bio': 'Software developer',
'country': 'us',
'newsletter': True
}
form.process(**user_data) # Pre-populate the form
if form.validate_on_submit():
# Process the updated data
first_name = form.first_name.data
# ... save to database
return 'Profile updated!'
return render_template('edit_profile.html', form=form)
Django Connection: Direct Parallels
1. Form Class Definition
Flask-WTF:
python
from flask_wtf import FlaskForm
from wtforms import StringField, validators
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[
validators.DataRequired(),
validators.Length(min=3, max=50)
])
Django:
python
from django import forms
class RegistrationForm(forms.Form):
username = forms.CharField(
label='Username',
max_length=50,
min_length=3,
required=True
)
2. Validation
Flask-WTF:
python
email = StringField('Email', validators=[
DataRequired(),
Email()
])
Django:
python
email = forms.EmailField(
label='Email',
required=True
)
3. CSRF Protection
Flask-WTF:
html
<form method="POST">
{{ form.hidden_tag() }} <!-- Automatically includes CSRF token -->
<!-- form fields -->
</form>
Django:
html
<form method="POST">
{% csrf_token %} <!-- Django's CSRF token -->
<!-- form fields -->
</form>
4. Form Handling in Views
Flask-WTF:
python
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# Process valid form
return redirect('/success')
return render_template('register.html', form=form)
Django:
python
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# Process valid form
return redirect('/success')
else:
form = RegistrationForm()
return render(request, 'register.html', {'form': form})
5. Template Rendering
Flask-WTF:
html
{{ form.username.label }}
{{ form.username() }}
{% for error in form.username.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
Django:
html
{{ form.username.label_tag }}
{{ form.username }}
{% for error in form.username.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
Key Parallels to Understand:
-
Form Class Structure: Both use Python classes to define form structure and validation
-
Field Types: Similar field types (StringField/CharField, EmailField, etc.)
-
Validation Logic: Centralized validation in the form class
-
CSRF Protection: Automatic CSRF protection
-
Template Rendering: Similar template syntax for rendering forms and errors
-
Form Handling Pattern: Same basic pattern in views (GET=show form, POST=validate+process)
Comments (0)
No comments yet. Be the first to comment!