fastapi:wtforms库的应用

一,官方站

库地址:

https://pypi.org/project/WTForms/

代码站:

https://github.com/pallets-eco/wtforms/

文档:

https://wtforms.readthedocs.io/en/3.2.x/

二,代码:

python

router = APIRouter(prefix="/test", tags=["测试"])


# 1. 定义 WTForms 表单类
class DemoForm(WTForm):
    # Select 元素:下拉选择框
    # choices 接收一个由 (值, 显示文本) 组成的元组列表
    city = SelectField(
        "选择城市",
        choices=[(0, "北京"), (1, "上海"), (2, "广州")], default=0,
        validators=[DataRequired(message="请选择一个城市")],
    )

    # Radio 元素:单选框
    gender = RadioField(
        "性别",
        choices=[(1, "男"), (0, "女")],default=1,
        validators=[DataRequired(message="请选择性别")],
    )

    # Textarea 元素:多行文本输入框
    bio = TextAreaField(
        "个人简介", validators=[DataRequired(message="请输入个人简介")]
    )

# 2. GET 路由:渲染包含空表单的页面
@router.api_route("/demo", methods=["GET", "POST"], response_class=HTMLResponse)
async def show_form(request: Request):


    if request.method == "GET":
        form = DemoForm()
        return templates.TemplateResponse(
            request=request,
            name="test/demo.html",
            context={
                "request": request, "form": form, "title": "登录"
            }
        )
    elif request.method == "POST":
        # 将 FastAPI 接收到的 FormData 转换为 WTForms 需要的格式
        form_data = await request.form()

        form = DemoForm(form_data)

        if form.validate():

            print("city:", form.city.data)
            print("gender:", form.gender.data)
            print("bio:", form.bio.data)

            # 返回成功
            return JSONResponse({"status_code": 200, "detail": "提交成功", "data": {}})
        else:
            print(form.errors)
            error_msg = "; ".join([f"{field}: {', '.join(errors)}" for field, errors in form.errors.items()])
            # 返回异常
            return JSONResponse({"status_code": 400, "detail": error_msg, "data": {}})

html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>FastAPI + WTForms 动态表单</title>
    <!-- 引入简易 CSS 框架让页面好看一点 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@example/water.css@2/out/water.css">
    <style>
        .error-msg { color: #ff4d4f; font-size: 0.9em; margin-top: 5px; }
        .radio-group { display: flex; gap: 15px; align-items: center; margin: 10px 0; }
        .radio-item { display: inline-flex; align-items: center; gap: 5px; }
        .success-box { background-color: #e6f7ff; border: 1px solid #91d5ff; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
        .form-radio {
    list-style-type: none;
    padding-inline-start: 0px;
    padding-top: 6px;
    display: flex;
    gap: 20px;
}

    </style>

</head>
<body>

    <h2>FastAPI + WTForms 动态表单示例</h2>

    <!-- 显示提交成功后的结果 -->
    {% if result %}
        <div class="success-box">
            <strong>{{ result }}</strong>
        </div>
    {% endif %}

    <form method="POST" action="">

        <!-- 1. 动态生成 Select 下拉框 -->
        <div style="margin-bottom: 20px;">
            <label>{{ form.city.label }}</label>
            {{ form.city(class="form-select") }}
            {% if form.city.errors %}
                {% for error in form.city.errors %}
                    <div class="error-msg">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>

        <!-- 2. 动态生成 Radio 单选框 Radio 需要循环遍历里面的选项单独渲染
        <div style="margin-bottom: 20px;">
            <label>{{ form.gender.label }}</label>
            <div class="radio-group">

                {% for subfield in form.gender %}
                    <span class="radio-item">
                        {{ subfield }}
                        {{ subfield.label }}
                    </span>
                {% endfor %}
            </div>
            {% if form.gender.errors %}
                {% for error in form.gender.errors %}
                    <div class="error-msg">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>
        -->

        <!-- 2. 动态生成 Radio 单选框 -->
        <div style="margin-bottom: 20px;">
            <label>{{ form.gender.label }}</label>
            {{ form.gender(class="form-radio") }}
            {% if form.gender.errors %}
                {% for error in form.gender.errors %}
                    <div class="error-msg">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>

        <!-- 3. 动态生成 Textarea 多行文本 -->
        <div style="margin-bottom: 20px;">
            <label>{{ form.bio.label }}</label>
            <!-- 可以在括号里直接控制 rows 和 cols 等 HTML 属性 -->
            {{ form.bio(rows="5", style="width: 100%;") }}
            {% if form.bio.errors %}
                {% for error in form.bio.errors %}
                    <div class="error-msg">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>

        <button type="submit">提交表单</button>
    </form>

</body>
</html>

 

三,测试效果:

页面:

image

接收到的数据:

city: 1
gender: 0
bio: 上海,女

 

posted @ 2026-07-26 22:20  刘宏缔的架构森林  阅读(10)  评论(0)    收藏  举报