You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django ORM跨库查询注解DateTimeField时出现'tzinfo'属性错误

跨数据库模型注解DateTime字段触发AttributeError错误

问题背景

在Django项目中配置了数据库路由,需对存储在另一数据库的关联模型查询集使用注解,采用字典作为临时处理方案。注解datetime字段时触发错误:AttributeError: 'str' object has no attribute 'tzinfo'。注:Deal模型的delivery_time字段已定义为DateTimeField。

错误堆栈

in localize(self, dt, is_dst)
    229     def localize(self, dt, is_dst=False):
    230         '''Convert naive time to local time'''
--> 231         if dt.tzinfo is not None:
    232             raise ValueError('Not naive datetime (tzinfo is already set)')
    233         return dt.replace(tzinfo=self)

AttributeError: 'str' object has no attribute 'tzinfo'

原代码示例

from app1.models import Deal 
from app2.models import Contract 
from django.utils import timezone as django_timezone 
from django.db.models import Case, DateTimeField, Value, When


deal_qs = Deal.objects.filter(<filters relevant to the flow>).values('id', 'final_amount', 'delivery_time', 'status__name')
deal_dict = {deal['id']: deal for deal in deal_qs} 


trade_object_ids = list(deal_dict.keys())
contracts = Contract.objects.filter(trade_object_id__in=trade_object_ids)


# 注解定义部分
delivery_date_annotation = Case(
    *[When(
        trade_object_id=key,
        then=Value(
            django_timezone.make_aware(
                django_timezone.datetime.strptime(
                    django_timezone.datetime.strftime(
                        deal_dict.get(key, {}).get('delivery_time'), "%Y-%m-%d %H:%M:%S"
                    ),
                    "%Y-%m-%d %H:%M:%S"
                ),
                django_timezone.get_current_timezone()
            )
        )
        if deal_dict.get(key, {}).get('delivery_time')
        else None
    )
        for key in trade_object_ids
    ],
    default=Value(None),
    output_field=DateTimeField()
)

contracts = contracts.annotate(my_date_time_field=delivery_date_annotation)

问题分析

核心原因是:使用values()查询返回的字典中,DateTimeField类型的字段会被序列化为字符串(而非datetime对象)。原代码中对该字符串做了strftime再strptime的无效转换,最终传给django_timezone.make_aware()的仍然是字符串,而非datetime对象,导致触发tzinfo属性不存在的错误。

解决方案

方案1:修复字典处理逻辑

直接将values()返回的字符串格式时间转换为datetime对象,无需多余的格式化操作:

delivery_date_annotation = Case(
    *[When(
        trade_object_id=key,
        then=Value(
            # 直接解析字符串为带时区的datetime对象
            django_timezone.parse(deal_dict[key]['delivery_time'])
        )
        if deal_dict[key].get('delivery_time')
        else None
    ) for key in trade_object_ids],
    default=Value(None),
    output_field=DateTimeField()
)

如果需要手动指定格式(避免解析错误),可以替换为:

django_timezone.make_aware(
    django_timezone.datetime.strptime(deal_dict[key]['delivery_time'], "%Y-%m-%d %H:%M:%S"),
    django_timezone.get_current_timezone()
)

方案2:使用ORM跨数据库关联(更优雅)

如果数据库路由允许,通过定义跨数据库外键,结合Subquery实现注解,避免手动处理字典:

  1. 在Contract模型中添加跨数据库外键:
class Contract(models.Model):
    trade_object_id = models.IntegerField()
    # 跨数据库关联Deal模型,需指定数据库别名
    deal = models.ForeignKey(
        'app1.Deal',
        on_delete=models.DO_NOTHING,
        db_constraint=False,  # 取消数据库级约束(跨数据库不支持)
        to_field='id',
        db='deal_db',  # 替换为Deal模型所在的数据库别名
        related_name='contracts',
        blank=True,
        null=True
    )
  1. 使用Subquery注解:
from django.db.models import Subquery, OuterRef

# 子查询获取对应Deal的delivery_time
deal_subquery = Deal.objects.filter(id=OuterRef('trade_object_id')).values('delivery_time')[:1]
contracts = Contract.objects.filter(trade_object_id__in=trade_object_ids).annotate(
    my_date_time_field=Subquery(deal_subquery, output_field=DateTimeField())
)

内容的提问来源于stack exchange,提问作者abhigya shridhar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 02:43:12