從零開始:Flask表單處理與WTForms驗證

在Web應用中,表單是用戶與服務器交互的核心方式之一——比如登錄、註冊、提交反饋等場景都需要表單。Flask本身不直接提供表單處理功能,但通過擴展庫Flask-WTF(基於WTForms),可以輕鬆實現表單創建、數據驗證和安全處理。本文將從零開始,一步步帶你掌握Flask中表單處理與驗證的核心知識。

一、環境準備:安裝必要庫

首先確保安裝了FlaskFlask-WTF。打開終端,執行以下命令:

pip install flask flask-wtf
  • Flask:Web框架基礎。
  • Flask-WTF:Flask的表單擴展,內置了WTForms(強大的表單處理庫)和CSRF保護(防止跨站請求僞造攻擊)。

二、核心概念:表單類與驗證器

我們通過表單類定義表單結構和驗證規則,每個字段(如用戶名、密碼)對應一個類屬性,每個屬性可以附加多個驗證器(如非空、郵箱格式、長度限制等)。

1. 定義表單類

以“用戶註冊”表單爲例,創建一個RegistrationForm類,繼承FlaskForm(Flask-WTF的基類)。每個字段通過StringFieldPasswordField等類型定義,並用validators參數添加驗證規則。

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length, EqualTo

class RegistrationForm(FlaskForm):
    # 用戶名:非空,長度4-20
    username = StringField('用戶名', 
                          validators=[DataRequired(), Length(min=4, max=20)])

    # 郵箱:非空,格式驗證
    email = StringField('郵箱', 
                       validators=[DataRequired(), Email()])

    # 密碼:非空,長度8-20
    password = PasswordField('密碼', 
                           validators=[DataRequired(), Length(min=8, max=20)])

    # 確認密碼:與密碼一致,非空
    confirm_password = PasswordField('確認密碼', 
                                    validators=[DataRequired(), EqualTo('password', 
                                                                       message='兩次密碼輸入不一致')])

    # 提交按鈕
    submit = SubmitField('註冊')

關鍵說明
- StringField:普通文本輸入框(如用戶名、郵箱)。
- PasswordField:密碼輸入框(自動隱藏輸入內容)。
- SubmitField:提交按鈕。
- validators:驗證器列表,每個驗證器負責檢查字段的合法性:
- DataRequired():字段不能爲空。
- Email():驗證郵箱格式。
- Length(min, max):限制字符串長度。
- EqualTo('field'):確保與指定字段值一致(如確認密碼)。

三、在Flask中渲染表單

定義好表單類後,需要在視圖函數中實例化表單,並在模板中渲染。

1. 視圖函數處理表單

app.py中定義視圖函數,處理GET/POST請求:

from flask import Flask, render_template, redirect, url_for, flash
from forms import RegistrationForm  # 導入表單類

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'  # 用於CSRF保護,需隨機字符串

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()  # 實例化表單類
    if form.validate_on_submit():  # 檢查是否爲POST請求且數據合法
        # 數據合法:獲取表單數據(如form.username.data)
        username = form.username.data
        email = form.email.data
        password = form.password.data

        # 這裏可添加後續操作(如存入數據庫)
        flash('註冊成功!')  # 提示信息(需在模板中顯示)
        return redirect(url_for('index'))  # 重定向到首頁
    return render_template('register.html', form=form)  # 渲染模板,傳入表單

if __name__ == '__main__':
    app.run(debug=True)

2. 模板渲染表單(register.html)

使用Jinja2模板語法渲染表單,需注意:
- form.hidden_tag():生成CSRF令牌(Flask-WTF自動添加,防止惡意提交)。
- 顯示字段標籤、輸入框和錯誤信息。

<!DOCTYPE html>
<html>
<head>
    <title>註冊</title>
</head>
<body>
    <h1>用戶註冊</h1>
    <!-- 顯示提示信息 -->
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            {% for message in messages %}
                <div style="color: green;">{{ message }}</div>
            {% endfor %}
        {% endif %}
    {% endwith %}

    <!-- 渲染表單 -->
    <form method="POST">
        {{ form.hidden_tag() }}  <!-- 必須添加,否則CSRF驗證失敗 -->

        <!-- 用戶名 -->
        <div>
            {{ form.username.label }}  <!-- 標籤 -->
            {{ form.username(size=32) }}  <!-- 輸入框 -->
            <!-- 顯示錯誤信息 -->
            {% for error in form.username.errors %}
                <span style="color: red;">{{ error }}</span>
            {% endfor %}
        </div>

        <!-- 郵箱 -->
        <div>
            {{ form.email.label }}
            {{ form.email(size=64) }}
            {% for error in form.email.errors %}
                <span style="color: red;">{{ error }}</span>
            {% endfor %}
        </div>

        <!-- 密碼 -->
        <div>
            {{ form.password.label }}
            {{ form.password(size=32) }}
            {% for error in form.password.errors %}
                <span style="color: red;">{{ error }}</span>
            {% endfor %}
        </div>

        <!-- 確認密碼 -->
        <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>

        <!-- 提交按鈕 -->
        <div>{{ form.submit() }}</div>
    </form>
</body>
</html>

四、處理表單提交與驗證

當用戶提交表單時,Flask-WTF會自動完成以下步驟:
1. 檢查請求方法是否爲POST。
2. 驗證CSRF令牌(防止僞造請求)。
3. 運行所有validators檢查數據合法性。
4. 若所有驗證通過,form.validate_on_submit()返回True,可處理數據。

關鍵流程

  • GET請求:渲染空表單(用戶首次訪問)。
  • POST請求
  • 若驗證通過:獲取數據(form.xxx.data),執行後續操作(如存入數據庫)。
  • 若驗證失敗:模板中顯示錯誤信息(通過form.xxx.errors循環輸出)。

五、完整示例:從表單到數據庫

若需將用戶數據存入數據庫,可結合SQLAlchemy(Flask的ORM擴展)。這裏簡化示例,假設已安裝flask-sqlalchemy

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():
        # 檢查用戶名/郵箱是否已存在(可選)
        existing_user = User.query.filter_by(username=form.username.data).first()
        if existing_user:
            flash('用戶名已存在!')
            return redirect(url_for('register'))
        # 創建新用戶並保存
        new_user = User(
            username=form.username.data,
            email=form.email.data,
            password=form.password.data  # 實際項目中需加密存儲
        )
        db.session.add(new_user)
        db.session.commit()
        flash('註冊成功!')
        return redirect(url_for('index'))
    return render_template('register.html', form=form)

六、總結

Flask表單處理的核心步驟:
1. 定義表單類:用FlaskForm和WTForms字段,添加驗證器。
2. 視圖函數處理:實例化表單,判斷請求方法,驗證數據。
3. 模板渲染:用form.hidden_tag()生成CSRF令牌,顯示字段和錯誤信息。
4. 數據處理:驗證通過後,獲取數據並執行後續操作(如數據庫存儲)。

通過WTForms的驗證器,可輕鬆實現非空、格式、長度等校驗,結合Flask-WTF的CSRF保護,能快速構建安全可靠的表單系統。

提示:初學者可先從簡單場景(如登錄表單)入手,逐步掌握複雜驗證(如文件上傳、多字段關聯)。Flask-WTF文檔(https://flask-wtf.readthedocs.io/)和WTForms文檔(https://wtforms.readthedocs.io/)是進階學習的好資源。

小夜