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

Django预订系统编辑预订时的时段重叠校验问题求解

Django航空器预订系统编辑页时段重叠校验修复

问题说明

基于Django框架开发航空器预订系统,Booking模型已完成定义,新建预订功能运行正常,仅编辑预订的时段重叠校验逻辑存在异常,需满足两个核心规则:

  • 编辑预订时若未修改核心预订数据,允许表单直接保存
  • 编辑预订时若修改了核心数据,仅当修改后的预订与其他已有预订无时段重叠时才允许保存
    系统自定义uid字段,通过拼接飞机ID、预订日期、起止时间生成预订唯一标识,用于判断核心字段是否变更。

原有问题代码

模型定义 models.py

class Booking(models.Model):
    aircraft = models.ForeignKey(Aircraft, on_delete=models.CASCADE)
    student = models.ForeignKey(
        Student, on_delete=models.CASCADE, blank=True, null=True)
    instructor = models.ForeignKey(
        Instructor, on_delete=models.CASCADE, blank=True, null=True)
    renter = models.ForeignKey(
        Renter, on_delete=models.CASCADE, blank=True, null=True)
    booking_date = models.DateField()
    start_time = models.TimeField()
    end_time = models.TimeField()
    uid = models.CharField(max_length=200, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

视图逻辑 views.py(原有错误版本)

if request.method == "POST":
        aircraft = request.POST.get('aircraft')
        student = request.POST.get('student')
        instructor = request.POST.get('instructor')
        renter = request.POST.get('renter')
        date = request.POST.get('date')
        start_time = request.POST.get('start_time')
        end_time = request.POST.get('end_time')
        try:
            booking.aircraft_id = aircraft
            booking.student_id = student
            booking.instructor_id = instructor
            booking.renter_id = renter
            booking.booking_date = date
            booking.start_time = start_time
            booking.end_time = end_time
            booking.uid = uid_for_booking(aircraft, date, start_time, end_time)

            case_1 = Booking.objects.filter(
                aircraft=aircraft, booking_date=date, start_time__lt=start_time, end_time__gt=start_time).exists()
            case_2 = Booking.objects.filter(
                aircraft=aircraft, booking_date=date, start_time__lte=end_time, end_time__gte=end_time).exists()
            case_3 = Booking.objects.filter(
                aircraft=aircraft, booking_date=date, start_time__gte=start_time, end_time__lte=end_time).exists()
            if (case_1 or case_2 or case_3) and (booking_obj.uid != booking.uid):
                messages.error(request, 'Aircraft Already Booked')
                return HttpResponseRedirect(reverse('booking:edit_booking', kwargs={'pk': pk}))
            elif booking.uid == booking_obj.uid:
                booking.save()
            else:
                booking.save()
            messages.success(request, "Booking Successfully Edited")
            return HttpResponseRedirect(reverse('booking:booking_index', kwargs={'data': date}))
        except:
            messages.error(request, "Failed to Edit Booking")
            return HttpResponseRedirect(reverse('booking:booking_index', kwargs={'pk': pk}))

UID生成方法

def uid_for_booking(a, b, c, d):
    aircraft = str(a)
    date = str(b)
    start_time = str(c)
    end_time = str(d)
    uid = aircraft + date + start_time + end_time
    return uid

问题根因

  1. 核心逻辑缺陷:重叠校验查询未排除当前正在编辑的预订本身,当用户修改时间、飞机等核心字段时,数据库中存储的该条预订旧数据会被判定为冲突记录,导致合法修改被拦截
  2. 代码冗余:三段式重叠判断逻辑重复,维护成本高,标准时段重叠判断可通过单条查询实现
  3. 分支冗余:UID相等和不相等的两个分支都执行save()操作,无实际分支意义
  4. 异常捕获不规范:裸except会捕获所有异常(包括系统级异常),吞掉具体报错信息,不利于问题排查
  5. 跳转错误:异常分支跳转到booking_index时传了pk参数,和路由定义的data参数不匹配,会导致跳转404

修复后实现

修复思路

  • 用标准时段重叠判断规则:两个时段存在重叠的充要条件为新预订开始时间 < 已有预订结束时间 AND 新预订结束时间 > 已有预订开始时间
  • 重叠查询时通过exclude(pk=pk)排除当前编辑的预订记录,避免将自身旧数据判定为冲突
  • 先判断核心字段是否变更(通过新旧UID对比),未变更直接保存;变更后再执行重叠校验
  • 移除冗余分支,收窄异常捕获范围,修正跳转参数错误

修复后的视图代码

from django.shortcuts import get_object_or_404
from django.db import IntegrityError

if request.method == "POST":
    # 取到当前要编辑的数据库原始预订记录
    booking = get_object_or_404(Booking, pk=pk)
    original_uid = booking.uid

    # 提取POST提交参数
    aircraft_id = request.POST.get('aircraft')
    student_id = request.POST.get('student')
    instructor_id = request.POST.get('instructor')
    renter_id = request.POST.get('renter')
    booking_date = request.POST.get('date')
    start_time = request.POST.get('start_time')
    end_time = request.POST.get('end_time')

    try:
        # 给实例赋新提交的值
        booking.aircraft_id = aircraft_id
        booking.student_id = student_id
        booking.instructor_id = instructor_id
        booking.renter_id = renter_id
        booking.booking_date = booking_date
        booking.start_time = start_time
        booking.end_time = end_time
        new_uid = uid_for_booking(aircraft_id, booking_date, start_time, end_time)
        booking.uid = new_uid

        # 规则1:核心字段无变更,直接保存
        if new_uid == original_uid:
            booking.save()
            messages.success(request, "Booking Successfully Edited")
            return HttpResponseRedirect(reverse('booking:booking_index', kwargs={'data': booking_date}))

        # 规则2:核心字段变更,校验时段重叠,关键:排除当前预订自身
        has_overlap = Booking.objects.filter(
            aircraft_id=aircraft_id,
            booking_date=booking_date,
            start_time__lt=end_time,
            end_time__gt=start_time
        ).exclude(pk=pk).exists()

        if has_overlap:
            messages.error(request, 'Aircraft Already Booked')
            return HttpResponseRedirect(reverse('booking:edit_booking', kwargs={'pk': pk}))

        # 校验通过,保存数据
        booking.save()
        messages.success(request, "Booking Successfully Edited")
        return HttpResponseRedirect(reverse('booking:booking_index', kwargs={'data': booking_date}))
    except (ValueError, IntegrityError):
        # 仅捕获可预期的参数格式错误、数据库约束错误
        messages.error(request, "Failed to Edit Booking")
        return HttpResponseRedirect(reverse('booking:edit_booking', kwargs={'pk': pk}))

可选优化建议

  • 可在Booking模型层添加clean方法实现重叠校验,避免视图层漏写校验导致脏数据入库
  • 将uid_for_booking实现为模型的静态方法,统一维护UID生成逻辑,避免散落在视图层
  • 时间字段建议先转换为datetime.time类型再做比较、拼接,避免字符串格式不一致导致UID判断失效

内容的提问来源于stack exchange,提问作者Giorgio Scarso

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:39:13