You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:55:23