fastapi:jinja2: 用WTForms 在 Jinja2 里快速生成 HTML 标签

一,安装第三方库:

$ pip install WTForms

二,代码

python:

# app/api/users.py
from fastapi import APIRouter, Depends
from starlette.requests import Request
from starlette.templating import Jinja2Templates

from app.core.Settings import get_settings
from app.core.templates import templates
from app.middleware.log_single_route import log_this_route
from wtforms import Form as WTForm, StringField, PasswordField, validators
from fastapi.responses import HTMLResponse
from wtforms.validators import DataRequired, Length

# 创建路由器实例
# prefix:所有路由的统一前缀,避免重复写"/users"
# tags:在Swagger文档中分组显示
router = APIRouter(prefix="/users", tags=["用户管理"])

# 1. 定义 WTForms 表单
class RegistrationForm(WTForm):
    username = StringField('用户名', [validators.Length(min=4, max=25)])
    password = PasswordField('密码', validators=[
            DataRequired(message="用户名111不能为空!"),
            Length(min=6, max=20, message="用户名长度必须在 6 到 20 之间")
        ])


# 2. GET 路由:渲染空表单
@router.get("/register", response_class=HTMLResponse)
async def get_register(request: Request):
    form = RegistrationForm()
    return templates.TemplateResponse(
        request=request,
        name="users/register.html",
        context={"request": request, "form": form,})


# 3. POST 路由:处理表单提交
@router.post("/register", response_class=HTMLResponse)
async def post_register(request: Request):
    # 将 FastAPI 接收到的 FormData 转换为 WTForms 需要的格式
    form_data = await request.form()
    form = RegistrationForm(form_data)

    # 验证数据
    if form.validate():
        return HTMLResponse(content=f"<h1>注册成功!欢迎 {form.username.data}</h1>")

    # 验证失败,将带有错误信息的表单重新渲染回页面
    return templates.TemplateResponse(
        request=request,
        name="users/register.html",
        context={"request": request, "form": form,})

html:

<!DOCTYPE html>
<html>
<head>
    <title>WTForms 快速生成示例</title>


</head>
<body>
    <h2>用户注册</h2>
    <form method="POST">
        <!-- 渲染用户名组件 -->
        <div>
            {{ form.username.label }} <br>
            {{ form.username(class="input-field", placeholder="输入4-25位用户名") }}
            {% if form.username.errors %}
                <span style="color: red;">{{ form.username.errors[0] }}</span>
            {% endif %}
        </div>

        <br>

        <!-- 渲染密码组件 -->
        <div>
            {{ form.password.label }} <br>
            {{ form.password(placeholder="输入密码") }}
            {% if form.password.errors %}
                <span style="color: red;">{{ form.password.errors[0] }}</span>
            {% endif %}
        </div>

        <br>
        <button type="submit">提交</button>
    </form>

<script>
    const passwordInput = document.getElementById('password');

    // 1. 监听验证失败事件
    passwordInput.addEventListener('invalid', function (e) {
        // 可以根据具体的错误类型给予不同的提示
        if (this.validity.tooShort) {
            this.setCustomValidity('密码长度不够,请至少输入6位字符。');
        } else if (this.validity.valueMissing) {
            this.setCustomValidity('密码111不能为空!');
        }
    });

    // 2. 监听输入事件,实时清除错误状态
    passwordInput.addEventListener('input', function () {
        this.setCustomValidity('');
    });
</script>
</body>
</html>

 

 

三,测试效果:

image

posted @ 2026-07-12 23:16  刘宏缔的架构森林  阅读(4)  评论(0)    收藏  举报