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>
三,测试效果:

浙公网安备 33010602011771号