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

Django:如何忽略年份按日/月范围过滤课程可用时段

课程可用时段存储与过滤方案解析

一、存储方案对比

针对无需考虑年份的可选可用时段,两种存储方案的优缺点如下:

方案1:存储两个日期字段(如available_start_at、available_end_at)

  • 优势:
    • 利用数据库原生日期类型,自动校验日期合法性(避免出现2月30日这类无效日期)
    • ORM层面支持直接使用日期相关查询API,代码更简洁
  • 劣势:存储时需填充冗余年份(可统一用固定年份如2000年),查询时需额外处理年份忽略逻辑

方案2:存储四个数字字段(from_day、from_month、to_day、to_month)

  • 优势:完全剥离年份,字段语义直接,无冗余数据
  • 劣势:需自行实现日期合法性校验,ORM查询时需拼接大量条件,代码复杂度更高

结论:优先选择方案1,数据库日期类型的合法性校验和便捷的ORM查询是核心优势,年份问题可通过统一固定年份或查询时提取月日解决。

二、当前日期过滤可用课程逻辑实现

需求:忽略年份,判断当前月日是否在课程时段范围内,同时支持跨年时段(如12/20至次年1/10)的过滤。

以下是完整的Django ORM实现代码:

from django.db.models import Q, F
import datetime

def get_available_courses(self):
    today = datetime.date.today()
    today_month = today.month
    today_day = today.day

    # 判断时段是否跨年(结束月日早于开始月日)
    is_year_crossed = Q(available_end_at__month__lt=F('available_start_at__month')) | \
                      Q(available_end_at__month=F('available_start_at__month'), 
                        available_end_at__day__lt=F('available_start_at__day'))

    # 非跨年时段:当前月日需在开始和结束之间
    filter_not_year_crossed = (
        # 当前 >= 开始月日
        Q(available_start_at__month__lt=today_month) |
        Q(available_start_at__month=today_month, available_start_at__day__lte=today_day)
    ) & (
        # 当前 <= 结束月日
        Q(available_end_at__month__gt=today_month) |
        Q(available_end_at__month=today_month, available_end_at__day__gte=today_day)
    )

    # 跨年时段:当前月日 >= 开始月日 或 当前月日 <= 结束月日
    filter_year_crossed = (
        Q(available_start_at__month__lt=today_month) |
        Q(available_start_at__month=today_month, available_start_at__day__lte=today_day)
    ) | (
        Q(available_end_at__month__gt=today_month) |
        Q(available_end_at__month=today_month, available_end_at__day__gte=today_day)
    )

    # 最终过滤规则:无时段限制 或 符合对应时段的可用条件
    final_filter = Q(available_start_at__isnull=True, available_end_at__isnull=True) | \
                   (is_year_crossed & filter_year_crossed) | \
                   (~is_year_crossed & filter_not_year_crossed)

    return self.account.get_courses().filter(final_filter)

关键逻辑说明

  • 使用F()表达式在数据库层面比较开始和结束的月日,避免内存处理数据,提升查询效率
  • 非跨年时段需同时满足「当前晚于等于开始月日」和「当前早于等于结束月日」
  • 跨年时段只需满足「当前晚于等于开始月日(到当年年底)」或「当前早于等于结束月日(当年年初到结束日)」
  • 支持未设置时段的课程(默认全部可用)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 12:40:31