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

Django预约系统优化:仅展示可用时段技术求助

Django预约应用:仅显示可用时段实现方案

问题描述

开发Django预约应用时,需要让用户只能查看未被占用的可用时段,而非全部时段,现有代码无法实现该功能,寻求解决方法。

现有代码

models.py

YEAR_CHOICES = ['2023','2024','2025']

TIME_CHOICES = [
    (0,'2am-3am'),
    (1,'3am-4am'),
    (3,'4am-5am'),
    (4,'5am-6am'),
    (5,'6am-7am'),
    (6,'7am-8am'),
    (7,'8am-9am'),
]
 
class Appointments(models.Model):
    full_name = models.CharField(max_length=40)
    mobile_number = models.IntegerField()
    email = models.EmailField(max_length=100)
    my_day = models.DateField(null=True)
    my_time = models.CharField(null=True,max_length=100,choices=TIME_CHOICES)
    
    def __str__(self):
        return f"{self.full_name} your appointment is schdeuled on {self.my_day} at {self.my_time}, Please check you email"

forms.py

YEAR_CHOICES = ['2023','2024','2025']

TIME_CHOICES = (
    ('2am-3am','2am-3am'),
    ('3am-4am','3am-4am'),
    ('4am-5am','4am-5am'),
    ('5am-6am','5am-6am'),
    ('6am-7am','6am-7am'),
    ('7am-8am','7am-8am'),
    ('8am-9am','8am-9am'),
)

class AppointmentForm(forms.Form):
    full_name = forms.CharField(label='Full Name',max_length=40)
    mobile_number = forms.IntegerField(label='Mobile Number')
    email = forms.EmailField(label='Email Address')
    my_day = forms.DateField(label= 'Choose day',widget=forms.SelectDateWidget(years=YEAR_CHOICES))
    my_time = forms.ChoiceField(label='Choose time',choices=TIME_CHOICES)

    def __str__(self):
        return f"{self.full_name} your appointment is scheduled on {self.my_day} at {self.my_time}"

views.py

def appointment(request):
    if request.method == "POST":
       form = AppointmentForm(request.POST)

       if form.is_valid():
           print(form.cleaned_data)
           obj = Appointments()
           obj.full_name = form.cleaned_data['full_name']
           obj.mobile_number = form.cleaned_data['mobile_number']
           obj.email = form.cleaned_data['email']
           obj.my_day = form.cleaned_data['my_day']
           obj.my_time = form.cleaned_data['my_time']
            
           data = Appointments.objects.last()
           context = {'object':data}
  
           existing_appointment = Appointments.objects.filter(my_time=obj.my_time,my_day=obj.my_day)
           
           if existing_appointment.exists():
                
                # Handle the case where the appointment already exists
                messages.success(request, "The Selected Time Has Been Reserved Before!")
                return render(request, 'myapp/appointment_exists.html')
           
           else:
                obj.save()

           message = f"Dear {form.cleaned_data['full_name']} \n \n Your appointment is confirmed on {form.cleaned_data['my_day']} at {form.cleaned_data['my_time']} \n \n Looking to see you soon :)"
           recep_email = form.cleaned_data['email']
           send_mail(
               'Your appointment is confirmed!',
               message,
               EMAIL_HOST_USER,
               ['ahmedabouzied@live.com',recep_email]
           )
            
           return render(request,'myapp/appointment_thankyou.html',context) 
    else:
        form = AppointmentForm()
    return render(request,'myapp/appointment.html',context={'form':form})

appointment.html

<form method="POST">
    {% csrf_token %}
    {{form.as_p}}
    <input type="submit">
</form>

实现方案

1. 统一时段选项

首先修正models和forms中TIME_CHOICES不一致的问题,确保数据库存储的时段值和表单选项匹配:

修改models.py:

YEAR_CHOICES = ['2023','2024','2025']

# 和forms保持一致的时段选项
TIME_CHOICES = (
    ('2am-3am','2am-3am'),
    ('3am-4am','3am-4am'),
    ('4am-5am','4am-5am'),
    ('5am-6am','5am-6am'),
    ('6am-7am','6am-7am'),
    ('7am-8am','7am-8am'),
    ('8am-9am','8am-9am'),
)
 
class Appointments(models.Model):
    full_name = models.CharField(max_length=40)
    mobile_number = models.IntegerField()
    email = models.EmailField(max_length=100)
    my_day = models.DateField(null=True)
    # 调整字段长度适配时段字符串
    my_time = models.CharField(null=True, max_length=20, choices=TIME_CHOICES)
    
    def __str__(self):
        return f"{self.full_name} 您的预约已安排在 {self.my_day} {self.my_time},请查收邮件"

修改forms.py,直接导入models中的时段选项避免重复定义:

from django import forms
from .models import TIME_CHOICES

YEAR_CHOICES = ['2023','2024','2025']

class AppointmentForm(forms.Form):
    full_name = forms.CharField(label='姓名', max_length=40)
    mobile_number = forms.IntegerField(label='手机号')
    email = forms.EmailField(label='邮箱地址')
    my_day = forms.DateField(label='选择日期', widget=forms.SelectDateWidget(years=YEAR_CHOICES))
    my_time = forms.ChoiceField(label='选择时段', choices=TIME_CHOICES)

    def __str__(self):
        return f"{self.full_name} 您的预约已安排在 {self.my_day} {self.my_time}"

2. 后端动态过滤可用时段

修改views.py,在GET请求时根据用户选择的日期过滤已占用时段,POST提交时再次校验防止并发冲突:

from django.shortcuts import render
from django.core.mail import send_mail
from django.contrib import messages
from datetime import datetime
from .forms import AppointmentForm
from .models import Appointments, TIME_CHOICES
from django.conf import settings

def appointment(request):
    if request.method == "POST":
        form = AppointmentForm(request.POST)
        if form.is_valid():
            cleaned_data = form.cleaned_data
            # 二次校验时段是否被占用,避免并发预约冲突
            if Appointments.objects.filter(
                my_day=cleaned_data['my_day'],
                my_time=cleaned_data['my_time']
            ).exists():
                messages.error(request, "该时段已被预约,请选择其他时段!")
                # 重新生成带可用时段的表单
                booked_times = Appointments.objects.filter(my_day=cleaned_data['my_day']).values_list('my_time', flat=True)
                form.fields['my_time'].choices = [c for c in TIME_CHOICES if c[0] not in booked_times]
                return render(request, 'myapp/appointment.html', {'form': form})
            
            # 保存预约记录
            obj = Appointments.objects.create(**cleaned_data)
            # 发送确认邮件
            message = f"亲爱的 {cleaned_data['full_name']}:\n\n您的预约已确认,时间为 {cleaned_data['my_day']} {cleaned_data['my_time']}\n\n期待您的到来!"
            send_mail(
                '预约确认通知',
                message,
                settings.EMAIL_HOST_USER,
                [settings.EMAIL_HOST_USER, cleaned_data['email']]
            )
            return render(request, 'myapp/appointment_thankyou.html', {'object': obj})
    else:
        # GET请求,根据传入的日期参数过滤可用时段
        selected_day = request.GET.get('my_day')
        available_times = TIME_CHOICES
        if selected_day:
            try:
                selected_day = datetime.strptime(selected_day, '%Y-%m-%d').date()
                # 获取该日期已被占用的时段
                booked_times = Appointments.objects.filter(my_day=selected_day).values_list('my_time', flat=True)
                # 筛选出可用时段
                available_times = [choice for choice in TIME_CHOICES if choice[0] not in booked_times]
            except ValueError:
                # 日期格式错误,显示全部时段
                pass
        # 初始化表单并设置可用时段选项
        form = AppointmentForm()
        form.fields['my_time'].choices = available_times
    
    return render(request, 'myapp/appointment.html', {'form': form})

3. 前端动态更新时段选项

修改appointment.html,添加JavaScript监听日期选择事件,实时更新可用时段:

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="提交预约">
</form>

<script>
const daySelect = document.getElementById('id_my_day');
const timeSelect = document.getElementById('id_my_time');

// 监听日期选择变化
daySelect.addEventListener('change', function() {
    const selectedDay = this.value;
    if (!selectedDay) return;
    
    // 发送请求获取对应日期的可用时段
    fetch(`?my_day=${selectedDay}`)
        .then(response => response.text())
        .then(html => {
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = html;
            // 更新时段选择框的选项
            timeSelect.innerHTML = tempDiv.querySelector('#id_my_time').innerHTML;
        })
        .catch(err => console.error('加载可用时段失败:', err));
});
</script>

核心逻辑说明

  • 统一选项:确保数据库和表单的时段选项一致,避免数据匹配错误。
  • 动态过滤:后端根据用户选择的日期,从总时段中排除已被预约的时段,仅展示可用选项。
  • 并发防护:提交预约时再次校验时段状态,防止多个用户同时预约同一时段。
  • 实时更新:前端通过AJAX实现日期选择后自动刷新可用时段,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:48:10