如何在Django Annotate中计算工作日
在Django的annotate()中计算工作日天数
要直接在annotate()中实现工作日统计,需要借助数据库层面的日期函数(避免Python循环),这样才能基于计算结果直接筛选。不同数据库的实现方式略有差异,以下是主流数据库的解决方案:
PostgreSQL 实现
PostgreSQL支持generate_series生成日期序列,结合EXTRACT(DOW)判断星期几(0=周日,6=周六),统计非周末天数:
from django.db.models import F, Func, IntegerField from django.db.models.functions import Now class BusinessDays(Func): function = 'SUM' template = """ (SELECT COUNT(*) FROM generate_series(%(start_date)s, %(end_date)s, '1 day'::interval) AS d WHERE EXTRACT(DOW FROM d) NOT IN (0, 6)) """ output_field = IntegerField() # 调用示例:annotate后可直接筛选 table_values = Table.objects.annotate( business_days=BusinessDays( start_date=F('event__created'), end_date=Now() ) ).filter(business_days__gte=5)
MySQL 实现
MySQL没有直接生成日期序列的函数,通过计算总天数减去周末天数来得到工作日数:
from django.db.models import F, Func, IntegerField from django.db.models.functions import Now class BusinessDaysMySQL(Func): output_field = IntegerField() template = """ DATEDIFF(%(end_date)s, %(start_date)s) + 1 - FLOOR((DATEDIFF(%(end_date)s, %(start_date)s) + WEEKDAY(%(start_date)s) + 1) / 7) * 2 - CASE WHEN WEEKDAY(%(start_date)s) = 5 THEN 1 ELSE 0 END - CASE WHEN WEEKDAY(%(end_date)s) = 6 THEN 1 ELSE 0 END """ # 调用示例 table_values = Table.objects.annotate( business_days=BusinessDaysMySQL( start_date=F('event__created'), end_date=Now() ) ).filter(business_days__gte=5)
关键优势
- 直接在数据库层面计算,避免Python循环遍历,性能更优
annotate后可直接用filter基于工作日数筛选结果,无需二次处理
内容的提问来源于stack exchange,提问作者Muhammad Hammad
相关产品推荐
相关产品推荐

