FullCalendar拖拽事件时数据库随机重复问题求助
解决FullCalendar拖拽事件时Django后端出现重复记录的问题
1. 立即修复重复校验逻辑的明显错误
你的视图中重复校验代码存在逻辑矛盾:
if EncomendasCalendar.objects.filter(NumDoc=event_id, Ano=year).exclude(NumDoc=event_id).exists():
filter(NumDoc=event_id) 已经筛选出所有NumDoc匹配的记录,再用exclude(NumDoc=event_id)会把这些记录全部排除,导致永远无法检测到重复。正确逻辑应该是排除当前正在更新的事件实例,通过主键区分:
# 修正后的重复校验 if EncomendasCalendar.objects.filter(NumDoc=event_id, Ano=year).exclude(pk=event.pk).exists(): print("Duplicate") return JsonResponse({'success': False, 'error': 'An event with the same DocNum and Year already exists'}, status=400)
2. 数据库层面添加唯一约束(最可靠的防重复手段)
代码校验可能存在并发漏洞,必须在数据库层面对NumDoc+Year组合添加唯一约束,从根源阻止重复记录。在Django模型中添加:
class EncomendasCalendar(models.Model): NumDoc = models.CharField(max_length=...) # 你的字段定义 Ano = models.IntegerField() DataEntrega = models.DateTimeField() UpdatedBy = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) # 其他字段... class Meta: constraints = [ models.UniqueConstraint( fields=['NumDoc', 'Ano'], name='unique_numdoc_ano' ) ]
执行python manage.py makemigrations和python manage.py migrate生成数据库约束,此后若有重复数据操作,数据库会直接报错,后端可捕获并返回提示。
3. 防止前端重复发送请求
拖拽事件可能因用户快速操作多次触发,导致AJAX请求并发。在前端添加请求锁避免重复提交:
let isUpdating = false; // 添加全局标记 eventDrop: function (info) { if (isUpdating) { info.revert(); alert('操作正在处理中,请稍后再试'); return; } isUpdating = true; var eventId = info.event.id; var eventYear = info.event.extendedProps.year; var newStartDate = info.event.start.toISOString().replace('T', ' ').replace('Z', ''); var csrftoken = Cookies.get('csrftoken'); $.ajax({ type: 'POST', url: 'update_delivery_date/', data: { id: eventId, new_start_date: newStartDate, year: eventYear }, beforeSend: function(xhr) { xhr.setRequestHeader("X-CSRFToken", csrftoken); }, success: function(response) { // 成功逻辑 }, error: function(xhr, status, error) { if (xhr.status === 403) { alert('Error: You do not have permission to move this event.'); } else { console.error('Error updating the delivery date:', error); } info.revert(); // 出错时恢复事件位置 }, complete: function() { isUpdating = false; // 请求完成后重置标记 } }); }
4. 确保事务与行锁有效
你已使用transaction.atomic()和select_for_update(),可显式设置事务隔离级别强化锁机制:
from django.db import transaction @csrf_exempt def update_delivery_date(request): if request.method == 'POST': # ... 权限检查等代码 try: with transaction.atomic(): transaction.set_isolation_level(transaction.REPEATABLE_READ) event = EncomendasCalendar.objects.select_for_update().get(NumDoc=event_id, Ano=year) # ... 后续校验和更新逻辑 # ... 异常处理
同时确认SQL Server的READ_COMMITTED_SNAPSHOT选项已启用,确保行锁正常生效。
额外排查点
- 验证FullCalendar的
event.id是否完全匹配数据库中的NumDoc,避免因ID不匹配导致后端找不到原事件; - 通过SQL Server日志或
UpdatedBy字段,确认重复记录是否由拖拽操作触发,排除其他业务逻辑的影响。
内容的提问来源于stack exchange,提问作者maxipedro
相关产品推荐
相关产品推荐

