Django序列化timedelta报无isoformat属性错误解决方案
报错根因
两个核心问题触发异常:
- 字段类型不匹配:
Sum聚合模型中存储时长的work_time字段(对应ORM的DurationField,Python侧类型为datetime.timedelta)得到的total值是时间差对象,但序列化器中total被声明为serializers.TimeField。DRF的TimeField序列化时会主动调用值的isoformat()方法,而timedelta类型本身没有实现该方法,直接抛出你看到的属性错误。 - 查询集逻辑和序列化器不匹配:你在查询集中调用了
.values(week=TruncWeek('timestamp'))做分组聚合,返回的结果字典仅包含week和total两个字段,序列化器声明的id、timestamp、work_time、user字段都不存在,就算解决类型问题,后续序列化也会触发字段缺失错误。
修复步骤
1. 替换total字段的序列化类型
累计工时是时间差,不是单日的时刻值,禁止使用TimeField处理(该类型无法正确表示超过24小时的时长,比如累计26小时会被错误显示为2点),二选一即可:
- 快速适配:直接使用DRF内置的
DurationField,自动将timedelta序列化为标准ISO时长格式:# 替换原来的total = serializers.TimeField() total = serializers.DurationField(read_only=True) - 自定义格式:如果需要输出固定的
HH:MM:SS格式,自定义序列化字段做转换:class HmsDurationField(serializers.Field): def to_representation(self, value): total_sec = int(value.total_seconds()) hour, rem = divmod(total_sec, 3600) minute, sec = divmod(rem, 60) return f"{hour:02d}:{minute:02d}:{sec:02d}" # 序列化器中total字段声明改为 total = HmsDurationField(read_only=True)
2. 对齐查询集和序列化器的字段
你当前的查询是周维度的聚合统计,不是返回单条StaffWorkTime模型实例,不要复用原模型序列化器,单独写统计用的序列化器即可,同时修正查询集的筛选、排序逻辑:
修正后的视图查询集代码:
def get_queryset(self): week_start = datetime.date.today() - datetime.timedelta(days=datetime.date.today().weekday()) # 原range筛选的week_end是当周周日0点,会漏掉周日当天的所有记录,补1天覆盖整周 week_end = week_start + datetime.timedelta(days=7) queryset = StaffWorkTime.objects.filter( user_id=self.kwargs['pk'], timestamp__gte=week_start, timestamp__lt=week_end ).annotate(week=TruncWeek('timestamp'))\ .values('week')\ .annotate(total=Sum('work_time'))\ .order_by('week') # 原order_by('timestamp')在分组后不存在该字段,会触发SQL错误 return queryset
配套的统计序列化器代码:
class StaffWeekWorkTimeStatSerializer(serializers.Serializer): week = serializers.DateTimeField(format="%d-%m-%Y", read_only=True) total = HmsDurationField(read_only=True) # 如果需要返回用户信息,可在values中加入user关联字段,再在此处声明对应序列化字段
视图中把serializer_class替换为上述统计序列化器即可。
避坑提示
不要尝试把timedelta强转为time类型处理总时长,一旦单周总工时超过24小时,转换结果必然出错,所有累计时长场景都要基于timedelta类型做序列化转换。
内容的提问来源于stack exchange,提问作者Biju
相关产品推荐
相关产品推荐

