如何基于科目、年级及多预约日期筛选可用家教?
解决基于科目、年级及多日期筛选未被预约家教的问题
问题背景
业务需求为:家长按科目、年级及多个预约日期搜索家教,需展示对应条件下未被预约的所有家教。现有Django模型及ViewSet代码存在逻辑缺陷,无法满足需求。
现有模型代码
TutorProfile 模型
class TutorProfile(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE) which_grade = models.ForeignKey( Grade, on_delete=models.CASCADE, blank=True, null=True) tutoring_subject = models.ForeignKey( TutoringSubject, on_delete=models.CASCADE, blank=True, null=True) sat = models.BooleanField(default=False) sat_time = models.CharField( _("Saturday Time:"), max_length=255, null=True, blank=True) sun = models.BooleanField(default=False) sun_time = models.CharField( _("Sunday Time:"), max_length=255, null=True, blank=True) mon = models.BooleanField(default=False) mon_time = models.CharField( _("Monday Time:"), max_length=255, null=True, blank=True) tue = models.BooleanField(default=False) tue_time = models.CharField( _("Tuesday Time:"), max_length=255, null=True, blank=True) wed = models.BooleanField(default=False) wed_time = models.CharField( _("Wednesday Time:"), max_length=255, null=True, blank=True) thu = models.BooleanField(default=False) thu_time = models.CharField( _("Thursday Time:"), max_length=255, null=True, blank=True) fri = models.BooleanField(default=False) fri_time = models.CharField( _("Friday Time:"), max_length=255, null=True, blank=True)
Booking 模型
class Booking(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE) book_day = models.CharField( _("Book Day"), max_length=255, null=True, blank=True) book_time = models.CharField( _("Book Time"), max_length=255, null=True, blank=True) status = models.CharField( _("Status"), max_length=255, null=True, blank=True)
现有代码核心问题
- Booking模型缺失关键关联:未关联
TutorProfile,无法记录具体是哪个家教被预约,导致无法筛选未被预约的家教。 - 多日期处理失效:循环中第一次满足条件就直接
return,仅处理第一个日期,忽略后续日期。 - 参数未利用:科目、年级参数未加入筛选逻辑,完全无效。
- 字段匹配错误:周五筛选错误使用
thu=1,应为fri=1。 - 未排除已预约家教:仅判断日期是否有预约,未关联到具体家教,无法排除已被预约的对象。
修复方案与代码实现
第一步:修正Booking模型
添加tutor_profile外键,关联家教与预约记录:
class Booking(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # 新增:关联对应的家教 tutor_profile = models.ForeignKey(TutorProfile, on_delete=models.CASCADE, related_name='bookings') book_day = models.CharField(_("Book Day"), max_length=255, null=True, blank=True) book_time = models.CharField(_("Book Time"), max_length=255, null=True, blank=True) status = models.CharField(_("Status"), max_length=255, null=True, blank=True)
第二步:重写get_queryset方法
实现完整的筛选逻辑:
from datetime import datetime from django.db.models import Q def get_queryset(self): subject_param = self.request.GET.get('subject') bookday_str = self.request.GET.get('book_day') grade_param = self.request.GET.get('grade') # 初始化基础查询集 queryset = TutorProfile.objects.all() # 筛选指定科目 if subject_param: queryset = queryset.filter(tutoring_subject_id=subject_param) # 筛选指定年级 if grade_param: queryset = queryset.filter(which_grade_id=grade_param) # 处理多日期筛选与排除已预约家教 if bookday_str: book_days = [day.strip() for day in bookday_str.split(",")] # 日期名称与模型字段映射 day_field_map = { "Monday": "mon", "Tuesday": "tue", "Wednesday": "wed", "Thursday": "thu", "Friday": "fri", "Saturday": "sat", "Sunday": "sun" } # 筛选所有指定日期都有空的家教(若需求为任意日期有空,可改为OR条件) for day_str in book_days: try: day_name = datetime.strptime(day_str, "%d-%m-%Y").strftime("%A") field_name = day_field_map.get(day_name) if field_name: queryset = queryset.filter(**{field_name: True}) except ValueError: # 跳过格式错误的日期 continue # 排除在指定日期已被预约的家教(可根据需求添加status过滤,如status="confirmed") booked_tutor_ids = Booking.objects.filter( book_day__in=book_days ).values_list('tutor_profile_id', flat=True) queryset = queryset.exclude(id__in=booked_tutor_ids) return queryset
关键逻辑说明
- 关联预约与家教:通过
Booking.tutor_profile建立关联,才能精准定位已被预约的家教。 - 多日期筛选:遍历所有指定日期,确保家教在每个日期都有可用时间(若需求为"任意日期有空",可将循环中的
filter改为Q对象的OR组合)。 - 排除已预约:通过
values_list获取所有已被预约的家教ID,用exclude从查询集中移除。 - 异常处理:捕获日期格式错误,避免单个无效日期导致整个查询失败。
- 参数复用:充分利用科目、年级参数,实现精准筛选。
内容的提问来源于stack exchange,提问作者Iqbal Hussain
相关产品推荐
相关产品推荐

