如何将Django的F表达式转换为字符串?(遗留代码场景)
解决Django中合并Date字段与字符串时间字段为DateTime格式的问题
问题根源
你代码里的核心错误是试图用Python的datetime.strptime处理F("start_time")——F()表达式是数据库层面的查询构造器,不会在Python运行时解析成实际的字段值,str(F("start_time"))得到的只是<F: start_time>这个字符串,自然无法正确解析时间。
两种可行解决方案
方案1:序列化器层面处理(兼容性强,无需依赖数据库)
如果数据量不是极大,推荐在序列化器里用Python代码处理,写法简单且不依赖数据库方言:
# 引入必要模块 from datetime import datetime import pytz from rest_framework import serializers class DateThingSerializer(serializers.ModelSerializer): start = serializers.SerializerMethodField() def get_start(self, obj): # 解析字符串时间为time对象 time_obj = datetime.strptime(obj.start_time, "%I:%M %p").time() # 合并date与time,添加UTC时区 full_datetime = datetime.combine(obj.date, time_obj, tzinfo=pytz.UTC) # 输出目标格式字符串 return full_datetime.isoformat() class Meta: model = DateThing fields = ("start",) # 按需添加其他字段
然后在ViewSet里指定序列化器即可:
class ViewSet(viewsets.GenericViewSet, mixins.ListModelMixin): queryset = DateThing.objects.all() serializer_class = DateThingSerializer
方案2:数据库层面Annotate(性能优,适合大数据量)
如果数据量较大,推荐直接在数据库层面完成转换,避免Python循环处理。需要根据你使用的数据库类型调整:
针对PostgreSQL
自定义数据库函数实现时间格式转换:
from django.db.models import F, Value, CharField from django.db.models.functions import Concat, Func # 自定义PostgreSQL的TO_TIMESTAMP函数封装 class ToTimestamp(Func): function = 'TO_TIMESTAMP' template = "%(function)s(%(expressions)s, '%(format)s')" def __init__(self, expression, format, **extra): super().__init__(expression, format=format, **extra) # 自定义PostgreSQL的TO_CHAR函数封装 class ToChar(Func): function = 'TO_CHAR' template = "%(function)s(%(expressions)s, '%(format)s')" def __init__(self, expression, format, **extra): super().__init__(expression, format=format, **extra) class ViewSet(viewsets.GenericViewSet, mixins.ListModelMixin): queryset = DateThing.objects.annotate( # 将12小时制字符串转成24小时制时间字符串 time_24h=ToChar(ToTimestamp(F('start_time'), 'HH12:MI AM'), 'HH24:MI'), # 合并所有部分成目标格式 start=Concat( F('date'), Value(' '), F('time_24h'), Value(':00+00:00'), output_field=CharField() ) ) # 后续指定序列化器时直接使用annotate后的start字段即可
针对MySQL
替换为MySQL的时间转换函数:
from django.db.models import F, Value, CharField from django.db.models.functions import Concat, Func class StrToDate(Func): function = 'STR_TO_DATE' template = "%(function)s(%(expressions)s, '%(format)s')" def __init__(self, expression, format, **extra): super().__init__(expression, format=format, **extra) class DateFormat(Func): function = 'DATE_FORMAT' template = "%(function)s(%(expressions)s, '%(format)s')" def __init__(self, expression, format, **extra): super().__init__(expression, format=format, **extra) class ViewSet(viewsets.GenericViewSet, mixins.ListModelMixin): queryset = DateThing.objects.annotate( time_24h=DateFormat(StrToDate(F('start_time'), '%h:%i %p'), '%H:%i'), start=Concat( F('date'), Value(' '), F('time_24h'), Value(':00+00:00'), output_field=CharField() ) )
验证结果
两种方案最终都会输出你期望的格式:
2022-11-01 20:30:00+00:00
内容的提问来源于stack exchange,提问作者Rio Weber
相关产品推荐
相关产品推荐

