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
相关产品推荐
相关产品推荐

