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

