最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何在 Flask Kanban 应用里通过拖放更新数据库状态
时间:2026-07-01 11:07:57 编辑:袖梨 来源:一聚教程网
本文详解如何在 flask + jinja2 构建的看板应用中,正确结合 html5 drag and drop api 与后端逻辑,实现拖拽任务卡片跨列(to-do/in progress/done)时实时更新数据库状态,并解决事件未触发、子元素干扰、服务端函数误执行等常见陷阱。
本文详解如何在 flask + jinja2 构建的看板应用中,正确结合 html5 drag and drop api 与后端逻辑,实现拖拽任务卡片跨列(to-do/in progress/done)时实时更新数据库状态,并解决事件未触发、子元素干扰、服务端函数误执行等常见陷阱。
在 Flask 看板(Kanban)应用中,仅靠前端拖放事件无法直接调用 Python 后端函数——Jinja2 模板中的 {{ dropped(...) }} 是服务端渲染时一次性求值的表达式,而非运行时可调用的 JavaScript 函数。因此,原代码中 drop(ev) 内直接写 {{ dropped(...) }} 实际上会在页面加载时就执行一次(并报错或静默失败),而不会响应用户拖放动作。
✅ 正确方案是:前端通过 fetch() 发起异步 POST 请求,将拖放数据(task_id + target_state)发送至 Flask 路由;后端接收 JSON 数据、更新数据库、提交事务,并返回重定向响应。
✅ 前端实现:事件绑定与 fetch 请求
首先修正 HTML 结构,确保 ondrop、ondragover 等事件绑定到 <ol> 容器(即 drop zone),并显式传入当前元素 this,避免事件冒泡到子节点:
<ol class="kanban To-do" id="todo" ondrop="drop(event, this)" ondragover="dragOver(event, this)" ondragenter="dragEnter(event, this)" ondragleave="dragLeave(event, this)"> <h2>To-Do</h2> {% for (depth, pending) in tasks_by_status[1] %} <li class="dd-item indent{{ depth }}" id="{{ pending.id }}" draggable="true" ondragstart="dragStart(event)" ondragend="dragEnd(event)"> <h3 class="title dd-handle"> <button name="task" value="{{ pending.id }}">{{ pending.title }}</button> </h3> <div class="text" contenteditable="true">{{ pending.description }}</div> </li> {% endfor %}</ol>
对应 JavaScript 需包含防子元素干扰的 enter/leave 计数器,以及基于 fetch 的状态更新逻辑:
<script> let counter = 0; function dragEnter(ev, el) { ev.preventDefault(); ev.stopPropagation(); counter++; el.style.opacity = "1.0"; el.style.zoom = "1.1"; } function dragLeave(ev, el) { ev.preventDefault(); ev.stopPropagation(); counter--; if (counter === 0) { el.style.opacity = "0.5"; el.style.zoom = "1.0"; } } function dragOver(ev) { ev.preventDefault(); // 必须阻止默认行为,否则 ondrop 不触发 } function dragStart(ev) { ev.dataTransfer.setData("task_id", ev.target.id); } const kanbanUrl = "{{ url_for('main.kanban', project_id=project.id) }}"; async function drop(ev, el) { ev.preventDefault(); const taskId = ev.dataTransfer.getData("task_id"); const targetState = el.id; // 如 "todo", "inprogress", "done" try { await fetch(kanbanUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ task_id: taskId, target_state: targetState }) }); location.reload(); // 成功后强制刷新,同步 UI 与 DB 状态 } catch (err) { console.error("Drag-drop update failed:", err); alert("状态更新失败,请重试"); } }</script>
⚠️ 注意:ondragover 必须调用 ev.preventDefault(),否则浏览器会阻止 drop 事件触发;dragEnter/dragLeave 中需 ev.stopPropagation() 防止子元素触发多次计数。
✅ 后端实现:统一路由处理 POST 请求
Flask 路由 /kanban 需同时支持 GET(渲染页面)和 POST(处理拖放更新)。关键点:
- 区分表单提交(如按钮点击)与 JSON 拖放请求;
- 从 request.json 提取参数,而非 request.form;
- 更新任务状态后 session.commit(),再重定向回当前看板页。
@main.route("/<int:project_id>/kanban", methods=["GET", "POST"])def kanban(project_id): project = Project.query.get(project_id) if not project: return render_template("404.html"), 404 try: session = sessions_by_project[project_id] except KeyError: return render_template("404.html"), 404 # 构建按状态分组的任务列表 tasks_by_status = defaultdict(list) for depth, task in walk_list(session.query(Task).all()): tasks_by_status[task.status.value].append((depth, task)) # 处理普通表单提交(如点击任务按钮) if request.method == "POST" and "task" in request.form: return redirect(url_for("main.task", project_id=project_id, id=int(request.form["task"]))) # ✅ 处理拖放 JSON 请求 if request.method == "POST" and request.is_json: data = request.get_json() if "task_id" in data and "target_state" in data: task_id = int(data["task_id"]) target_state = data["target_state"] task = session.query(Task).get(task_id) if not task: return render_template("404.html"), 404 task.change_status(str2status(target_state)) session.commit() # 返回重定向(前端 reload 已处理,此处可返回空响应或 success) return "", 204 # No Content,表示成功 return render_template("kanban.html", tasks_by_status=tasks_by_status, project=project)
? 关键总结与最佳实践
- 禁止模板内调用后端函数:{{ func(...) }} 是服务端渲染期执行,不能用于响应前端事件。
- 使用 fetch + JSON 通信:轻量、标准、易调试,比传统表单更契合拖放场景。
- Drop zone 必须阻止默认行为:ondragover 中 ev.preventDefault() 是 drop 触发的前提。
- 防子元素干扰:通过 counter + stopPropagation() 精确控制容器高亮状态。
- 状态同步策略:location.reload() 最简单可靠;进阶可采用局部 DOM 更新(需返回新 HTML 或 JSON 数据)。
- 错误处理不可少:前端捕获 fetch 异常,后端校验参数与数据库对象存在性。
至此,你的 Kanban 应用即可实现平滑、可靠的拖放状态更新——既保持 Flask 架构清晰,又充分发挥现代浏览器 API 的交互能力。
相关文章
- 三角洲行动s10拾荒者4任务攻略详解 07-01
- 狗狗币2025年7月能涨到多少价位 07-01
- 金砖弱网怎样彻底卸载-金砖弱网残留文件如何清理 07-01
- 快影怎样制作短视频 07-01
- 上海交警怎么处理他人车辆违章-上海交警非本人车辆违法代扣分如何办理 07-01
- 《斗罗大陆:魂师对决》五一游戏盛宴-多款精品手游推荐 07-01