Web Forms

Loading concept...

🎯 Flask Web Forms: Your Friendly Form Butler

Imagine you’re running a fancy restaurant. Guests fill out order slips (forms), and you need a butler to:

  1. Hand out the right slips 📝
  2. Check if orders make sense ✅
  3. Protect against sneaky imposters 🛡️

Flask-WTF is that butler. Let’s meet him!


🏠 Chapter 1: Meet Flask-WTF (Your Form Butler)

What is Flask-WTF?

Think of Flask-WTF as a helper robot that makes forms easy and safe.

Without it, you’d have to:

  • Write lots of boring HTML by hand
  • Check every input yourself
  • Worry about bad guys tricking your forms

With Flask-WTF, the robot does it all!

Installing Your Butler

pip install flask-wtf

Setting Up (Give Your Butler a Secret Key)

Your butler needs a secret password to protect forms:

from flask import Flask

app = Flask(__name__)
app.config['SECRET_KEY'] = 'my-super-secret'

🔑 Why a secret? It’s like a special stamp only YOU know. Stops bad guys from faking forms!


📝 Chapter 2: Creating Forms with WTForms

Your First Form Class

Instead of writing messy HTML, you describe forms in Python:

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField

class GreetingForm(FlaskForm):
    name = StringField('Your Name')
    submit = SubmitField('Say Hello')

What just happened?

  • FlaskForm = the blueprint maker
  • StringField = a text box
  • SubmitField = a button

Real Life: Like ordering pizza by checking boxes instead of writing a letter!

graph TD A[You describe form<br>in Python] --> B[Flask-WTF builds<br>HTML for you] B --> C[User fills it out] C --> D[Flask-WTF checks<br>everything]

🧰 Chapter 3: Form Field Types (Your Toolbox)

Your butler has MANY types of input boxes. Pick the right one!

Text Fields

Field What It Does Example Use
StringField Short text Username
TextAreaField Long text Bio, Comments
PasswordField Hidden dots Passwords
from wtforms import (
    StringField,
    TextAreaField,
    PasswordField
)

class ProfileForm(FlaskForm):
    username = StringField('Username')
    bio = TextAreaField('About You')
    password = PasswordField('Password')

Choice Fields

Field What It Does Example Use
SelectField Dropdown menu Country picker
RadioField Pick ONE circle Gender
BooleanField Checkbox “I agree”
from wtforms import (
    SelectField,
    RadioField,
    BooleanField
)

class PrefsForm(FlaskForm):
    color = SelectField(
        'Favorite Color',
        choices=[('r','Red'), ('b','Blue')]
    )
    agree = BooleanField('I Accept Terms')

Special Fields

Field What It Does
IntegerField Numbers only
FloatField Decimal numbers
DateField Calendar dates
EmailField Email addresses
FileField Upload files

🎨 Chapter 4: Rendering Forms in Templates

The Magic: {{ form.field }}

Your butler can paint forms in HTML automatically!

In your route:

@app.route('/greet', methods=['GET','POST'])
def greet():
    form = GreetingForm()
    return render_template('greet.html', form=form)

In your template (greet.html):

<form method="POST">
    {{ form.hidden_tag() }}

    {{ form.name.label }}
    {{ form.name() }}

    {{ form.submit() }}
</form>

What Does Each Part Do?

Code What It Does
form.hidden_tag() Adds secret protection (CSRF)
form.name.label Shows “Your Name” text
form.name() Creates the input box

Adding Styles (Make It Pretty!)

{{ form.name(class="my-input", placeholder="Enter name") }}

This creates:

<input class="my-input" placeholder="Enter name" ...>
graph TD A[Python Form Class] --> B[Pass to Template] B --> C["{{ form.field }}"] C --> D[Beautiful HTML<br>Input Box]

✅ Chapter 5: Form Validation Rules

Why Validate?

Imagine someone orders “pizza” but writes “🐱” in the quantity box.

Validation = Making sure inputs make sense!

Built-in Validators

from wtforms.validators import (
    DataRequired,
    Length,
    Email,
    EqualTo,
    NumberRange
)

class SignupForm(FlaskForm):
    username = StringField('Username', validators=[
        DataRequired(),
        Length(min=3, max=20)
    ])

    email = StringField('Email', validators=[
        DataRequired(),
        Email()
    ])

    password = PasswordField('Password', validators=[
        DataRequired(),
        Length(min=8)
    ])

    confirm = PasswordField('Confirm', validators=[
        EqualTo('password', message='Must match!')
    ])

    age = IntegerField('Age', validators=[
        NumberRange(min=13, max=120)
    ])

Validator Cheat Table

Validator What It Checks
DataRequired() Can’t be empty
Length(min, max) Text length limits
Email() Valid email format
EqualTo('field') Matches another field
NumberRange(min, max) Number between limits
Regexp(pattern) Matches a pattern
URL() Valid web address

Checking if Form is Valid

@app.route('/signup', methods=['GET','POST'])
def signup():
    form = SignupForm()

    if form.validate_on_submit():
        # All checks passed! 🎉
        username = form.username.data
        # Save to database...
        return redirect('/success')

    # Show form (with errors if any)
    return render_template('signup.html', form=form)

Showing Errors in Template

{{ form.username.label }}
{{ form.username() }}

{% for error in form.username.errors %}
    <span class="error">{{ error }}</span>
{% endfor %}

🔧 Chapter 6: Custom Validators

When Built-in Isn’t Enough

Sometimes you need special rules. Make your own!

Method 1: Inline Validator

Add a method starting with validate_:

from wtforms import ValidationError

class SignupForm(FlaskForm):
    username = StringField('Username')

    def validate_username(self, field):
        if field.data.lower() == 'admin':
            raise ValidationError(
                'Cannot use "admin" as username!'
            )

How it works:

  1. Name it validate_ + field name
  2. Check the field.data
  3. Raise ValidationError if bad

Method 2: Reusable Validator Function

def no_bad_words(form, field):
    bad = ['spam', 'fake', 'test']
    if field.data.lower() in bad:
        raise ValidationError(
            'Please use a real value!'
        )

class CommentForm(FlaskForm):
    text = TextAreaField('Comment', validators=[
        DataRequired(),
        no_bad_words  # Our custom one!
    ])

Method 3: Factory (With Parameters)

def must_contain(word):
    def _validator(form, field):
        if word not in field.data:
            raise ValidationError(
                f'Must contain "{word}"'
            )
    return _validator

class Form(FlaskForm):
    code = StringField('Code', validators=[
        must_contain('ABC')
    ])
graph TD A[User Submits Form] --> B{Built-in Validators} B -->|Pass| C{Custom Validators} B -->|Fail| E[Show Error] C -->|Pass| D[Success! ✅] C -->|Fail| E

🛡️ Chapter 7: CSRF Protection

The Sneaky Attack

Imagine a bad guy creates a fake website with a hidden form. When you visit, it secretly submits to YOUR bank!

This is CSRF (Cross-Site Request Forgery).

How Flask-WTF Stops It

  1. Secret Token: Your butler generates a unique “ticket”
  2. Hidden in Form: Added to every form you create
  3. Checked on Submit: If ticket is wrong = REJECTED!

The Magic Happens Automatically

When you use FlaskForm + form.hidden_tag():

<form method="POST">
    {{ form.hidden_tag() }}
    <!-- This adds a hidden input like: -->
    <!-- <input type="hidden" name="csrf_token" value="abc123..."> -->
</form>

The Flow

graph TD A[Your Server] -->|Gives Secret Token| B[Your Form] B -->|User Submits| C{Token Matches?} C -->|Yes ✅| D[Process Form] C -->|No ❌| E[Reject! Bad Guy Detected]

What You Must Do

  1. Set SECRET_KEY:
app.config['SECRET_KEY'] = 'hard-to-guess-string'
  1. Always use hidden_tag():
{{ form.hidden_tag() }}
  1. That’s it! Flask-WTF handles the rest.

For AJAX/API Forms

If you send forms with JavaScript:

<meta name="csrf-token" content="{{ csrf_token() }}">
// Include in your fetch headers:
headers: {
    'X-CSRFToken': document.querySelector(
        'meta[name="csrf-token"]'
    ).content
}

🎁 Putting It All Together

Here’s a complete example combining everything:

from flask import Flask, render_template, redirect
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length

app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-secret-key'

class LoginForm(FlaskForm):
    email = StringField('Email', validators=[
        DataRequired(),
        Email()
    ])
    password = PasswordField('Password', validators=[
        DataRequired(),
        Length(min=8)
    ])
    submit = SubmitField('Log In')

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()

    if form.validate_on_submit():
        # Form is valid!
        email = form.email.data
        # Check password, log in user...
        return redirect('/dashboard')

    return render_template('login.html', form=form)

Template (login.html):

<form method="POST">
    {{ form.hidden_tag() }}

    <div>
        {{ form.email.label }}
        {{ form.email(class="input-box") }}
        {% for e in form.email.errors %}
            <span class="error">{{ e }}</span>
        {% endfor %}
    </div>

    <div>
        {{ form.password.label }}
        {{ form.password(class="input-box") }}
        {% for e in form.password.errors %}
            <span class="error">{{ e }}</span>
        {% endfor %}
    </div>

    {{ form.submit(class="btn") }}
</form>

🚀 Quick Recap

Concept One-Liner
Flask-WTF Your form butler robot
WTForms Describe forms in Python
Field Types String, Password, Select, etc.
Rendering {{ form.field }} in templates
Validators Rules like Required, Email, Length
Custom Validators validate_fieldname() method
CSRF Secret tokens stop fake form attacks

🎉 You Did It!

You now have a friendly butler handling all your forms:

  • No messy HTML writing ✅
  • Automatic input checking ✅
  • Protection from bad guys ✅

Go build something awesome! 🚀

Loading story...

No Story Available

This concept doesn't have a story yet.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

Interactive Preview

Interactive - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Interactive Content

This concept doesn't have interactive content yet.

Cheatsheet Preview

Cheatsheet - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Cheatsheet Available

This concept doesn't have a cheatsheet yet.

Quiz Preview

Quiz - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Quiz Available

This concept doesn't have a quiz yet.