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

Django训练预约系统:预约后已预订时段未从可用列表消失的问题

问题分析与解决方案

即时修复:触发时段锁定逻辑

当前代码中,WorkingHour类定义了reserve_hour()方法用于标记时段为不可用,但预约流程中从未调用该方法,导致is_available字段始终保持True,已预约时段不会从可用列表中移除。

修改Views.py的form_valid方法,在保存预约后锁定对应时段:

class TrainerAppointmentCreate(CreateView):
    model = TrainerAppointment
    form_class = TrainerAppointmentForm
    template_name = 'create_appointment.html'
    success_url = reverse_lazy("home")

    def form_valid(self, form):
        if self.request.user.entries > 0:
            form.instance.user = self.request.user
            appointment = form.save()  # 保存预约并获取关联时段对象
            
            # 调用方法锁定时段
            appointment.chosen_hour.reserve_hour()
            
            self.request.user.entries -= 1
            self.request.user.save()

            return super().form_valid(form)
        else:
            form.add_error(None, "没有训练套餐,请先购买套餐。")
            return self.form_invalid(form)

核心设计缺陷修正(必做)

当前模型存在逻辑漏洞:WorkingHour的is_available是全局字段,意味着任意教练的某个时段被预约后,所有教练的该时段都会被标记为不可用,完全不符合实际业务逻辑(不同教练的时段应独立管理)。

优化方案:

  1. 添加唯一约束:在TrainerAppointment模型中,确保同一教练、同一日期、同一时段只能被预约一次:
class TrainerAppointment(Appointment):
    trainer = models.ForeignKey(Trainer, on_delete=models.CASCADE)
    services = models.ForeignKey(TrainerServices, on_delete=models.CASCADE)
    user = models.ForeignKey(MyUser, on_delete=models.CASCADE, null=True, blank=True)

    class Meta:
        unique_together = ('trainer', 'date', 'chosen_hour')  # 新增唯一约束

    def __str__(self):
        return f"{self.user}-{self.services}"
  1. 动态过滤可用时段:修改表单查询逻辑,排除当前教练在指定日期已被预约的时段,而非依赖全局的is_available字段:
class TrainerAppointmentForm(forms.ModelForm):
    chosen_hour = forms.ModelChoiceField(
        queryset=WorkingHour.objects.none(),  # 初始为空,后续动态填充
        empty_label=None,
        widget=forms.Select(attrs={"class": "form-select is_valid"})
    )

    class Meta:
        model = TrainerAppointment
        fields = "__all__"
        widgets = {
            "trainer": forms.Select(attrs={"class": "form-select is_valid"}),
            "date": forms.DateInput(attrs={"class": "form-control is_valid", "type": "date"}),
            "services": forms.Select(attrs={"class": "form-select is_valid"}),
        }

    def clean_date(self):
        date = self.cleaned_data.get('date')
        today = timezone.now().date()
        if date < today:
            raise forms.ValidationError("日期无效,请选择正确的日期。")
        return date

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 根据选中的教练和日期动态过滤可用时段
        trainer_id = self.data.get('trainer')
        date = self.data.get('date')
        if trainer_id and date:
            booked_hours = TrainerAppointment.objects.filter(
                trainer_id=trainer_id,
                date=date
            ).values_list('chosen_hour_id', flat=True)
            self.fields['chosen_hour'].queryset = WorkingHour.objects.exclude(id__in=booked_hours)
        else:
            # 默认显示所有时段(可结合Trainer的working_hours多对多关系进一步过滤)
            self.fields['chosen_hour'].queryset = WorkingHour.objects.all()
  1. 清理冗余字段:优化后不再需要WorkingHour中的is_available字段及reserve_hour方法,直接删除即可。

额外防并发建议

在表单中添加验证,避免并发提交导致的重复预约:

def clean(self):
    cleaned_data = super().clean()
    trainer = cleaned_data.get('trainer')
    date = cleaned_data.get('date')
    chosen_hour = cleaned_data.get('chosen_hour')
    
    if trainer and date and chosen_hour:
        if TrainerAppointment.objects.filter(
            trainer=trainer,
            date=date,
            chosen_hour=chosen_hour
        ).exists():
            raise forms.ValidationError("该时段已被预约,请选择其他时段。")
    return cleaned_data

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 22:20:36