Django实现动态编辑锁:用户活跃编辑时锁定实例阻止他人打开
Django编辑页动态锁实现方案
基础技术栈
- 后端:Django框架
- 前端:原生HTML/CSS/JavaScript,未引入React等交互式UI库
原有锁定逻辑
以实例X的编辑表单页为例,当前采用静态锁定规则:
- 用户打开编辑页面时,系统直接在数据库写入锁定标识,标记该实例已被当前编辑者锁定
- 仅当用户主动点击「保存」或「关闭」按钮完成操作后,系统才会解除锁定,开放给其他用户使用
现存问题
如果编辑者未保存内容、也未点击「关闭」按钮就直接离开页面,对应实例会一直保持锁定状态,必须等该用户返回手动解锁才能释放,其他用户无法正常编辑,严重影响协作效率。
目标动态锁定规则
核心逻辑为仅当用户处于活跃编辑状态时,才持有实例的编辑锁,其中「活跃」判定标准:
- 用户当前停留在编辑页面,且对应标签页在浏览器中处于激活状态
- 触发以下任意场景时,系统自动释放编辑锁,开放给其他用户:
- 用户直接关闭标签页/整个浏览器
- 标签页失活(切换到其他标签、最小化浏览器等)时长累计达到5分钟
具体实现
后端(Django侧)
- 改造原有锁数据表,在原有「关联实例ID」「锁定用户ID」字段基础上,新增3个字段:
last_heartbeat:DateTime类型,记录最近一次收到用户活跃心跳的时间lock_session_key:Char类型,记录持有锁的用户会话标识,避免同个用户多开标签页导致锁冲突is_valid:Boolean类型,标记锁是否处于有效状态
- 编写3个接口供前端调用:
acquire_lock加锁接口:用户首次进入编辑页时调用,先校验当前实例是否存在有效锁(即is_valid=True、last_heartbeat与当前时间差小于5分钟、锁不属于当前会话),如果存在有效锁直接返回「实例正在被其他用户编辑」的错误提示;如果不存在有效锁,则写入/更新锁记录,绑定当前会话,将last_heartbeat设为当前时间,返回加锁成功标识renew_lock心跳续约接口:前端定时调用,校验锁归属当前会话后,将last_heartbeat更新为当前时间即可release_lock主动解锁接口:用户点击保存/关闭按钮、页面正常关闭时调用,将对应锁记录的is_valid设为False
- 锁过期逻辑无需单独配置定时任务,每次收到加锁请求时,顺手将所有
last_heartbeat与当前时间差超过5分钟的锁标记为is_valid=False即可,足够支撑中小流量场景,减少运维成本。
前端(原生JS侧)
- 页面首次加载完成后,先调用
acquire_lock接口,加锁成功才渲染可编辑表单,加锁失败直接展示提示信息,禁止编辑。 - 监听
visibilitychange事件感知标签页激活状态:- 当
document.visibilityState === 'hidden'时,记录失活起始时间戳,暂停心跳续约请求 - 当
document.visibilityState === 'visible'时,计算累计失活时长,如果时长超过5分钟直接禁用表单,提示「编辑锁已过期,请刷新页面重新申请编辑权限」;如果时长未超过阈值,先调用一次renew_lock接口续约,再恢复心跳定时器
- 当
- 心跳续约逻辑:标签页处于激活状态时,每1分钟调用一次
renew_lock接口即可,无需过高频率减少服务端压力;如果接口返回锁已失效,直接禁用表单展示过期提示。 - 监听
beforeunload事件,用户关闭标签页、跳转其他页面时,通过navigator.sendBeacon调用release_lock接口主动解锁,该方法不会被浏览器页面卸载流程拦截,可靠性远高于普通AJAX请求。
踩坑提示:不要在
unload事件中绑定普通AJAX请求发送解锁信号,大多数浏览器会直接拦截卸载阶段的异步请求,导致解锁失败。
内容的提问来源于stack exchange,提问作者Yasser Mohsen
相关产品推荐
相关产品推荐

