Django 1.11下MySQL旧库中DateTime与整数秒字段的差值计算问题
这个场景我太熟悉了!面对遗留MySQL库不能改字段类型的情况,直接用Python的timedelta结合F()表达式确实会踩坑——毕竟timedelta是Python层面的东西,没法直接识别Django的查询表达式对象,才会出现那个AttributeError。下面给你两个靠谱的解决办法:
方法一:用数据库原生函数在SQL层面计算(推荐)
这个方法是让数据库来做时间转换和计算,效率高还支持后续的过滤/排序操作,完全适配Django 1.11。
步骤1:自定义秒转时间间隔的函数
利用MySQL的SEC_TO_TIME函数,把整数秒转成数据库能识别的时间格式:
from django.db.models import Func, ExpressionWrapper, F, DateTimeField class SecondsToTime(Func): function = 'SEC_TO_TIME' template = "%(function)s(%(expressions)s)"
步骤2:注解计算后的时间
假设你的模型里DateTime字段叫event_time,存秒数的Integer字段叫uptime_seconds,要计算event_time加上秒数后的时间,就这么写:
queryset = YourModel.objects.annotate( calculated_time=ExpressionWrapper( F('event_time') + SecondsToTime(F('uptime_seconds')), output_field=DateTimeField() ) )
如果是要计算两个时间的差值(比如当前时间和计算后时间的秒数差),可以用MySQL的TIMESTAMPDIFF函数:
class TimeDiffInSeconds(Func): function = 'TIMESTAMPDIFF' template = "%(function)s(SECOND, %(expressions)s, NOW())" output_field = IntegerField() queryset = YourModel.objects.annotate( time_elapsed=TimeDiffInSeconds(F('event_time') + SecondsToTime(F('uptime_seconds'))) )
方法二:Python层面处理(适合小数据集)
如果你的数据量不大,也可以在模型里加一个property属性,在Python层面完成计算:
from datetime import timedelta from django.db import models class YourModel(models.Model): event_time = models.DateTimeField() uptime_seconds = models.IntegerField() @property def calculated_time(self): return self.event_time + timedelta(seconds=self.uptime_seconds)
不过要注意:这个方法的计算是在Python内存中完成的,没法用在filter()或order_by()这类数据库层面的操作里,只适合直接获取对象时使用。
为什么你之前的方法会报错?
你尝试用timedelta(seconds=F('uptime_seconds'))是行不通的——timedelta是Python的内置类,它需要接收具体的数值,而F('uptime_seconds')是Django的查询表达式对象,不是实际的数字。Django在解析这个表达式时,会把CombinedExpression对象传给timedelta,最终导致了'CombinedExpression' object has no attribute 'split'的错误。
内容的提问来源于stack exchange,提问作者masterfloda

