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

如何更新顾问设置的可用时段?解决学生预约后时段更新需求

预约后顾问可用时段更新方案

现有模型与问题分析

现有模型代码

Setup_Availability 模型

from django.db import models
from userregistration.models import UserProfile

class Setup_Availability(models.Model):
    consultant = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='consultant_availability')
    start_date = models.DateField()  # 可用时段生效起始日期
    monday_start_time = models.TimeField(null=True, blank=True)
    monday_end_time = models.TimeField(null=True, blank=True)
    tuesday_start_time = models.TimeField(null=True, blank=True)
    tuesday_end_time = models.TimeField(null=True, blank=True)
    wednesday_start_time = models.TimeField(null=True, blank=True)
    wednesday_end_time = models.TimeField(null=True, blank=True)
    thursday_start_time = models.TimeField(null=True, blank=True)
    thursday_end_time = models.TimeField(null=True, blank=True)
    friday_start_time = models.TimeField(null=True, blank=True)
    friday_end_time = models.TimeField(null=True, blank=True)
    saturday_start_time = models.TimeField(null=True, blank=True)
    saturday_end_time = models.TimeField(null=True, blank=True)
    sunday_start_time = models.TimeField(null=True, blank=True)
    sunday_end_time = models.TimeField(null=True, blank=True)

    def __str__(self):
        return f"Availability for {self.consultant.user.username} starting on {self.start_date}"

Appointment 模型

class Appointment(models.Model):
    student = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='student_appointments')
    consultant = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='consultant_appointments')
    appointment_datetime = models.DateTimeField()
    appointment_start_time = models.TimeField()
    appointment_end_time = models.TimeField()
    is_available = models.BooleanField(default=True)

    def __str__(self):
        return f"{self.student.user.username} - {self.consultant.user.username} - {self.appointment_datetime}"

现有视图代码问题

def book_appointment(request):
    day_of_week_name = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
    if request.method == 'POST':
        form = AppointmentBookingForm(request.POST)
        if form.is_valid():
            consultant = form.cleaned_data['consultant']
            date = form.cleaned_data['date']
            appointment_start_time = form.cleaned_data['appointment_start_time']
            appointment_end_time = form.cleaned_data['appointment_end_time']

            # Check if the selected time slot is available
            availability = Setup_Availability.objects.get(consultant=consultant)
            day_of_week = date.weekday()
            available_start_time = getattr(availability, f'{day_of_week_name[day_of_week]}_start_time')
            available_end_time = getattr(availability, f'{day_of_week_name[day_of_week]}_end_time')

            selected_start_time = datetime.combine(date, appointment_start_time)
            selected_end_time = datetime.combine(date, appointment_end_time)

            if (available_start_time <= selected_start_time.time() < available_end_time and
                    available_start_time < selected_end_time.time() <= available_end_time):
                student_email = request.user.email
                consultant_email = consultant.user.email
                print(student_email)
                # The selected time slot is available, so create the appointment
                appointment = Appointment(
                    student=request.user.userprofile,
                    consultant=consultant,
                    appointment_datetime=selected_start_time,
                    appointment_start_time=selected_start_time,  # 错误:TimeField不能存储datetime对象
                    appointment_end_time=selected_end_time,  # 同上类型错误
                )
                appointment.save()

                # 错误:直接修改起始时间会丢失预约前的可用时段
                setattr(availability, f'{day_of_week_name[day_of_week]}_start_time', selected_end_time.time())
                availability.save()

                return redirect('appointment_success')
    else:
        form = AppointmentBookingForm()

    return render(request, 'book_appointment/book_appointment.html', {'form': form})

当前实现的核心问题

  1. 时段丢失:预约中间时段时(如顾问周一9:00-17:00,学生约10:00-12:00),直接修改起始时间会导致9:00-10:00的可用时段永久丢失,无法再次预约。
  2. 类型错误:Appointment的appointment_start_time和appointment_end_time为TimeField,但代码中赋值datetime对象,会抛出类型异常。
  3. 重复预约风险:仅检查顾问的初始可用时段,未验证该时段是否已被其他学生预约,可能导致时段冲突。
  4. 存储限制:Setup_Availability用单条记录存储一周所有时段,无法存储分割后的多个剩余可用时段。

可行解决方案

方案一:基于规则的动态可用检查(推荐)

将Setup_Availability作为顾问的基础可用规则(如每周固定时段),实时可用时段通过「基础规则 - 已预约时段」动态计算,无需修改原始规则记录。

修改后的视图逻辑

from django.db.models import Q
import datetime

def book_appointment(request):
    day_of_week_name = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
    if request.method == 'POST':
        form = AppointmentBookingForm(request.POST)
        if form.is_valid():
            consultant = form.cleaned_data['consultant']
            date = form.cleaned_data['date']
            appointment_start_time = form.cleaned_data['appointment_start_time']
            appointment_end_time = form.cleaned_data['appointment_end_time']

            # 1. 获取顾问生效的最新基础可用规则
            try:
                availability = Setup_Availability.objects.filter(
                    consultant=consultant,
                    start_date__lte=date
                ).latest('start_date')
            except Setup_Availability.DoesNotExist:
                form.add_error(None, "该顾问未设置可用时段")
                return render(request, 'book_appointment/book_appointment.html', {'form': form})

            # 2. 获取对应星期的基础可用时段
            day_of_week = date.weekday()
            available_start = getattr(availability, f'{day_of_week_name[day_of_week]}_start_time')
            available_end = getattr(availability, f'{day_of_week_name[day_of_week]}_end_time')

            if not (available_start and available_end):
                form.add_error(None, f"{date.strftime('%A')}该顾问无可用时段")
                return render(request, 'book_appointment/book_appointment.html', {'form': form})

            # 3. 转换为datetime对象便于时段比较
            selected_start = datetime.datetime.combine(date, appointment_start_time)
            selected_end = datetime.datetime.combine(date, appointment_end_time)
            available_start_dt = datetime.datetime.combine(date, available_start)
            available_end_dt = datetime.datetime.combine(date, available_end)

            # 4. 检查是否在基础可用时段范围内
            if not (available_start_dt <= selected_start and selected_end <= available_end_dt):
                form.add_error(None, "所选时段不在顾问可用范围内")
                return render(request, 'book_appointment/book_appointment.html', {'form': form})

            # 5. 检查该时段是否已被其他预约占用(重叠检测)
            overlapping = Appointment.objects.filter(
                consultant=consultant,
                appointment_datetime__date=date,
                Q(appointment_start_time__lt=appointment_end_time) &
                Q(appointment_end_time__gt=appointment_start_time)
            ).exists()

            if overlapping:
                form.add_error(None, "该时段已被预约")
                return render(request, 'book_appointment/book_appointment.html', {'form': form})

            # 6. 创建预约记录
            Appointment.objects.create(
                student=request.user.userprofile,
                consultant=consultant,
                appointment_datetime=selected_start,
                appointment_start_time=appointment_start_time,
                appointment_end_time=appointment_end_time
            )

            return redirect('appointment_success')
    else:
        form = AppointmentBookingForm()

    return render(request, 'book_appointment/book_appointment.html', {'form': form})

优点

  • 保留顾问的原始可用规则,无需修改基础设置
  • 动态计算剩余可用时段,避免数据丢失
  • 解决重复预约问题,逻辑严谨

方案二:重构模型为单时段记录

如果需要持久化存储剩余可用时段,可将Setup_Availability拆分为单个时段的独立记录,支持分割剩余时段。

重构后的模型

class AvailabilitySlot(models.Model):
    consultant = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='availability_slots')
    date = models.DateField()  # 具体日期
    start_time = models.TimeField()
    end_time = models.TimeField()
    is_active = models.BooleanField(default=True)  # 标记时段是否可用

    def __str__(self):
        return f"{self.consultant.user.username} - {self.date} {self.start_time}-{self.end_time}"

预约视图逻辑(简化版)

import datetime

def book_appointment(request):
    if request.method == 'POST':
        form = AppointmentBookingForm(request.POST)
        if form.is_valid():
            consultant = form.cleaned_data['consultant']
            date = form.cleaned_data['date']
            start_time = form.cleaned_data['appointment_start_time']
            end_time = form.cleaned_data['appointment_end_time']

            # 找到匹配的可用时段
            slot = AvailabilitySlot.objects.filter(
                consultant=consultant,
                date=date,
                is_active=True,
                start_time__lte=start_time,
                end_time__gte=end_time
            ).first()

            if not slot:
                form.add_error(None, "所选时段不可用")
                return render(request, 'book_appointment/book_appointment.html', {'form': form})

            # 创建预约记录
            Appointment.objects.create(
                student=request.user.userprofile,
                consultant=consultant,
                appointment_datetime=datetime.datetime.combine(date, start_time),
                appointment_start_time=start_time,
                appointment_end_time=end_time
            )

            # 分割剩余可用时段
            # 1. 保留原时段开头到预约开始的部分
            if slot.start_time < start_time:
                AvailabilitySlot.objects.create(
                    consultant=consultant,
                    date=date,
                    start_time=slot.start_time,
                    end_time=start_time
                )
            # 2. 保留预约结束到原时段结尾的部分
            if end_time < slot.end_time:
                AvailabilitySlot.objects.create(
                    consultant=consultant,
                    date=date,
                    start_time=end_time,
                    end_time=slot.end_time
                )
            # 3. 标记原时段为不可用
            slot.is_active = False
            slot.save()

            return redirect('appointment_success')
    else:
        form = AppointmentBookingForm()

    return render(request, 'book_appointment/book_appointment.html', {'form': form})

优点

  • 精确存储剩余可用时段,支持多次分割
  • 适合需要展示具体剩余时段的场景
  • 逻辑清晰,便于后续维护

内容的提问来源于stack exchange,提问作者Aashish Neupane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:37:03