Django聚合Jalali周乘客数问题:__week返回公历周数而非波斯历周数
解决方案
因为django_jalali的jDateField在数据库中仍以公历日期存储,Django默认的__week查询会调用数据库的公历周数函数,导致结果不符合预期。针对PostgreSQL环境,有以下几种可行方案:
方案1:直接使用PostgreSQL波斯历日期函数(推荐)
利用PostgreSQL的to_char函数,指定波斯历日历(NLS_CALENDAR=PERSIAN)来提取周数,通过Django的Func表达式实现:
from django.db.models import Func, F, Sum queryset = TravelReport.objects.annotate( jalali_week=Func( F('travel_date'), function='to_char', template="to_char(%(expressions)s, 'IW', 'NLS_CALENDAR=PERSIAN')" ) ).values('jalali_week').annotate(total_passengers=Sum('passengers'))
说明:
'IW'表示ISO标准周数(每周从周一开始),如果需要波斯历的其他周数格式,可替换为'%W'(每周从周日开始,周范围1-53),根据需求调整。- 该方案直接在数据库层面计算,效率高,适合大数据量场景。
方案2:自定义Jalali周数函数(复用性强)
封装一个可复用的自定义数据库函数,避免重复写SQL模板:
from django.db.models import Func class JalaliWeek(Func): function = 'to_char' template = "to_char(%(expressions)s, 'IW', 'NLS_CALENDAR=PERSIAN')" # 使用方式 queryset = TravelReport.objects.annotate( jalali_week=JalaliWeek(F('travel_date')) ).values('jalali_week').annotate(total_passengers=Sum('passengers'))
方案3:Python层面处理(仅小数据量适用)
如果数据量不大,可以先将数据查询到内存中,用jdatetime转换后分组聚合:
from jdatetime import date as jdate reports = TravelReport.objects.all() weekly_totals = {} for report in reports: # 将存储的公历日期转换为波斯历日期 jalali_date = jdate.fromgregorian(date=report.travel_date) week_num = jalali_date.isocalendar()[1] # 获取波斯历周数 if week_num not in weekly_totals: weekly_totals[week_num] = 0 weekly_totals[week_num] += report.passengers # 转换为类似QuerySet的结构 result = [{'week': k, 'total_passengers': v} for k, v in weekly_totals.items()]
注意:
- 此方案需要将所有数据加载到内存,数据量大时会严重影响性能,仅适合小规模数据场景。
内容的提问来源于stack exchange,提问作者Druid
相关产品推荐
相关产品推荐

