CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-flask-wtf

Form rendering, validation, and CSRF protection for Flask with WTForms.

72

0.91x
Overview
Eval results
Files

form-handling.mddocs/

Form Handling

Flask-integrated form processing with automatic request data binding, validation, and CSRF token management. Flask-WTF extends WTForms with Flask-specific features for seamless web form handling.

Capabilities

FlaskForm Class

Enhanced WTForms Form class with Flask integration, providing automatic data binding from Flask request context and built-in CSRF protection.

class FlaskForm(Form):
    def __init__(self, formdata=_Auto, **kwargs):
        """
        Initialize Flask-integrated form.
        
        Args:
            formdata: Form data source (_Auto for automatic Flask request binding,
                     None to disable, or custom MultiDict)
            **kwargs: Additional WTForms Form arguments
        """
    
    def is_submitted(self) -> bool:
        """
        Check if form was submitted via POST, PUT, PATCH, or DELETE request.
        
        Returns:
            True if current request method indicates form submission
        """
    
    def validate_on_submit(self, extra_validators=None) -> bool:
        """
        Validate form only if it was submitted.
        Shortcut for: form.is_submitted() and form.validate()
        
        Args:
            extra_validators: Additional validators to run
            
        Returns:
            True if form was submitted and validation passed
        """
    
    def hidden_tag(self, *fields) -> Markup:
        """
        Render form's hidden fields including CSRF token.
        
        Args:
            *fields: Specific fields to render (renders all hidden fields if none specified)
            
        Returns:
            HTML markup for hidden fields
        """

Form Base Class

Re-exported WTForms Form class for cases where Flask integration is not needed.

class Form:
    """
    Base WTForms Form class without Flask integration.
    Use FlaskForm for Flask applications.
    """

Usage Examples

Basic Form Creation

from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Length

class ContactForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired(), Length(min=2, max=50)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    message = TextAreaField('Message', validators=[DataRequired(), Length(min=10)])
    submit = SubmitField('Send Message')

Automatic Data Binding

FlaskForm automatically binds to Flask request data:

@app.route('/contact', methods=['GET', 'POST'])
def contact():
    form = ContactForm()  # Automatically binds to request.form, request.files, or request.json
    
    if form.validate_on_submit():
        # Form was submitted and validation passed
        name = form.name.data
        email = form.email.data
        message = form.message.data
        
        # Process form data
        send_email(name, email, message)
        flash('Message sent successfully!')
        return redirect(url_for('contact'))
    
    # GET request or validation failed
    return render_template('contact.html', form=form)

Custom Data Sources

from werkzeug.datastructures import MultiDict

# Disable automatic binding
form = ContactForm(formdata=None)

# Custom data source
custom_data = MultiDict([('name', 'John'), ('email', 'john@example.com')])
form = ContactForm(formdata=custom_data)

# JSON data handling (automatic for requests with content-type: application/json)
@app.route('/api/contact', methods=['POST'])
def api_contact():
    form = ContactForm()  # Automatically binds to request.json if content-type is JSON
    if form.validate():
        return {'status': 'success'}
    return {'status': 'error', 'errors': form.errors}, 400

Template Integration

<!-- Basic form template -->
<!DOCTYPE html>
<html>
<head>
    <title>Contact Form</title>
</head>
<body>
    <form method="POST">
        {{ form.hidden_tag() }}  <!-- Includes CSRF token and other hidden fields -->
        
        <div>
            {{ form.name.label(class="form-label") }}
            {{ form.name(class="form-control") }}
            {% for error in form.name.errors %}
                <div class="error">{{ error }}</div>
            {% endfor %}
        </div>
        
        <div>
            {{ form.email.label(class="form-label") }}
            {{ form.email(class="form-control") }}
            {% for error in form.email.errors %}
                <div class="error">{{ error }}</div>
            {% endfor %}
        </div>
        
        <div>
            {{ form.message.label(class="form-label") }}
            {{ form.message(class="form-control", rows="4") }}
            {% for error in form.message.errors %}
                <div class="error">{{ error }}</div>
            {% endfor %}
        </div>
        
        {{ form.submit(class="btn btn-primary") }}
    </form>
</body>
</html>

CSRF Token Management

# CSRF is automatically enabled for FlaskForm
class MyForm(FlaskForm):
    name = StringField('Name')
    
    # CSRF can be disabled for specific forms
    class Meta:
        csrf = False

# Custom CSRF configuration per form
class SecureForm(FlaskForm):
    data = StringField('Data')
    
    class Meta:
        csrf = True
        csrf_secret = 'form-specific-secret'
        csrf_time_limit = 1800  # 30 minutes
        csrf_field_name = 'security_token'

Multi-part Forms (File Uploads)

from flask_wtf.file import FileField
from wtforms import StringField

class UploadForm(FlaskForm):
    title = StringField('Title', validators=[DataRequired()])
    file = FileField('File')

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    form = UploadForm()  # Automatically handles request.files
    
    if form.validate_on_submit():
        title = form.title.data
        file = form.file.data  # Werkzeug FileStorage object
        
        if file:
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
        
        return redirect(url_for('success'))
    
    return render_template('upload.html', form=form)

Form Validation Patterns

# Custom validation
class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    confirm = PasswordField('Confirm Password', validators=[DataRequired()])
    
    def validate_username(self, field):
        """Custom field validator (method naming convention)"""
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('Username already exists.')
    
    def validate(self, extra_validators=None):
        """Custom form-level validation"""
        if not super().validate(extra_validators):
            return False
        
        if self.password.data != self.confirm.data:
            self.confirm.errors.append('Passwords must match.')
            return False
        
        return True

# Using with extra validators
@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    
    # Additional runtime validators
    extra_validators = {'username': [Length(min=3, max=20)]}
    
    if form.validate_on_submit(extra_validators):
        # Registration logic
        return redirect(url_for('login'))
    
    return render_template('register.html', form=form)

Configuration

Form Meta Configuration

from wtforms.meta import DefaultMeta

class CustomForm(FlaskForm):
    class Meta(DefaultMeta):
        # CSRF settings
        csrf = True
        csrf_secret = 'custom-csrf-key'
        csrf_field_name = 'csrf_token'
        csrf_time_limit = 3600
        
        # Localization
        locales = ['en_US', 'en']
        
        def get_translations(self, form):
            # Custom translation logic
            return super().get_translations(form)

Request Data Handling

Flask-WTF automatically handles different request data sources:

  1. Form Data: request.form (application/x-www-form-urlencoded)
  2. File Data: Combined request.files and request.form (multipart/form-data)
  3. JSON Data: request.json (application/json)

The data source is automatically selected based on the request content type and HTTP method.

Install with Tessl CLI

npx tessl i tessl/pypi-flask-wtf

docs

csrf-protection.md

file-upload.md

form-handling.md

index.md

recaptcha.md

tile.json