关于Django互斥并发编辑功能的实现方式与位置的疑问
嘿,我来帮你把这个并发编辑互斥的实现思路捋清楚,顺便给你明确下各个部分的代码位置~
一、先把模型层的逻辑补扎实
你已经在Order模型里加了locked字段和lock_edit方法,这是个很好的起点,但要注意数据库原子性——不然多个用户同时抢锁的时候,很可能出现“都读到锁是未占用,然后都改成锁定状态”的竞争问题。
优化后的模型代码可以这么写:
from django.db import models from django.db.models import F from django.utils import timezone from django.contrib.auth.models import User class Order(models.Model): edit_version = models.IntegerField(default=0, editable=True) # 用于乐观锁校验 locked = models.BooleanField(default=False) locked_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL) # 记录锁的持有者 locked_at = models.DateTimeField(null=True, blank=True) # 记录锁的创建时间,用于超时释放 def lock_edit(self, user): # 用数据库原子更新操作,确保只有当锁未被占用时才会成功锁定 updated_rows = Order.objects.filter(id=self.id, locked=False).update( locked=True, locked_by=user, locked_at=timezone.now() ) # 更新成功就刷新当前实例的字段,返回True;失败返回False if updated_rows > 0: self.refresh_from_db() return True return False def unlock_edit(self): # 同样用原子操作解锁 updated_rows = Order.objects.filter(id=self.id).update( locked=False, locked_by=None, locked_at=None ) if updated_rows > 0: self.refresh_from_db() return True return False
二、视图层是核心逻辑触发点
互斥控制的主要逻辑都要放在视图里,分两个关键时机处理:
1. 用户进入编辑页面时抢锁
当用户点击“编辑”按钮进入表单页,就要立刻尝试获取锁,避免多个用户同时进入编辑状态:
from django.shortcuts import get_object_or_404, redirect from django.contrib import messages from .models import Order from .forms import OrderForm def order_edit(request, pk): order = get_object_or_404(Order, pk=pk) # 处理GET请求:用户进入编辑页,尝试抢锁 if request.method == 'GET': # 先检查锁是否存在,并且是否超时(比如超过15分钟自动释放) if order.locked: if timezone.now() - order.locked_at > timezone.timedelta(minutes=15): order.unlock_edit() else: messages.error(request, f"该订单正在被{order.locked_by.username}编辑,请稍后再试") return redirect('order_detail', pk=pk) # 尝试获取锁,失败就提示用户 if not order.lock_edit(request.user): messages.error(request, "获取编辑锁失败,请刷新页面重试") return redirect('order_detail', pk=pk) # 处理POST请求:提交表单 form = OrderForm(request.POST or None, instance=order) if request.method == 'POST' and form.is_valid(): # 二次校验:确保当前用户仍然持有锁,防止锁超时后被其他人修改 if order.locked != True or order.locked_by != request.user: messages.error(request, "该订单已被其他用户修改或锁定,请刷新页面重新编辑") return redirect('order_edit', pk=pk) # 配合你原来的edit_version做乐观锁校验,防止提交过时数据 if form.instance.edit_version != order.edit_version: messages.error(request, "该订单已被更新,请刷新页面后重新编辑") return redirect('order_edit', pk=pk) # 保存数据,更新版本号,然后解锁 form.instance.edit_version += 1 form.save() order.unlock_edit() messages.success(request, "订单编辑成功") return redirect('order_detail', pk=pk) return render(request, 'order_edit.html', {'form': form, 'order': order})
2. 表单提交时的双重校验
即使进入页面时拿到了锁,提交前也要再次确认锁的状态——比如用户打开页面后离开太久,锁超时自动释放,这时候另一个用户修改了订单,当前用户的提交就会是过时数据,必须拦截。
三、模板层给用户直观提示
在订单列表或详情页,给用户显示锁的状态,避免无效操作:
<!-- 订单详情页示例 --> {% if order.locked %} <div class="alert alert-warning"> 🔒 该订单正在被{{ order.locked_by.username }}编辑,锁定时间:{{ order.locked_at|date:"Y-m-d H:i:s" }} {% if user == order.locked_by %} <a href="{% url 'order_edit' order.pk %}" class="btn btn-sm btn-primary ms-2">继续编辑</a> {% endif %} </div> {% else %} <a href="{% url 'order_edit' order.pk %}" class="btn btn-sm btn-primary">编辑订单</a> {% endif %}
四、额外优化建议
- 自动解锁定时任务:用Celery之类的工具做定时任务,定期清理超过30分钟的无效锁,防止订单被永久锁定
- 前端实时检查:在编辑页每隔1分钟发一次AJAX请求,检查锁是否仍有效,失效就及时提示用户
- 权限控制:在抢锁前先校验用户是否有编辑该订单的权限,避免无权限用户占用锁
这样一套流程下来,就能完美实现“同一表单不能被多用户同时编辑”的互斥效果啦~
内容的提问来源于stack exchange,提问作者pymat
相关产品推荐
相关产品推荐

