升级 Django 5.2 后,旧迁移导入 utc 报错该如何修复?
只要替换后保持相同的 UTC 时间语义,且不改变迁移名称、依赖关系、数据库操作或数据迁移行为,就可以直接修改自己维护的旧迁移,修复失效的 utc 导入。优先修复迁移文件;monkey patch 可以作为临时过渡,但会增加对启动顺序的依赖。
为什么已经执行过的迁移仍然报错?
Django 5.0 移除了 django.utils.timezone.utc,因此 Django 5.2 无法执行这条导入语句。Django 5.0 发布说明
makemigrations 需要加载历史迁移,构建迁移图并还原模型状态,再判断当前模型发生了什么变化。即使旧迁移已在数据库中执行,它的 Python 文件仍可能被导入,其中的顶层导入可能导致整个命令失败。Django 迁移加载器源码
Django 在加载旧迁移时就已经失败,因此仅新增迁移无法修复这个错误。
如何修改旧迁移?
原文件已经包含 import datetime,可以删除:
from django.utils.timezone import utc
并在导入区域之后定义:
utc = datetime.timezone.utc
这样可以保留后续代码中的 tzinfo=utc 等引用,减少修改范围。如果文件中完全没有使用 utc,直接删除失效的导入即可。
问题中的另一种写法也可以:
from datetime import timezone
utc = timezone.utc
如果文件里同时使用 Django 的 timezone 模块,需要避免名称覆盖,可以写成:
from datetime import timezone as datetime_timezone
utc = datetime_timezone.utc
这些建议适用于将 utc 用作 UTC 时区对象的常见场景。如果迁移还依赖具体时区对象的类型或对象身份,或者调用了 pytz 专用方法,就需要检查相关逻辑,不能只凭名称相似进行替换。
修改后会重新执行生产迁移吗?
Django 自带的迁移机制通过 django_migrations 表记录已应用状态,包括应用名、迁移名和应用时间,不保存迁移文件内容的校验和。因此,只要迁移标识和应用记录保持不变,修复导入就不会让已应用的迁移自动重新执行。Django 迁移记录器源码
历史迁移仍不能随意修改。新数据库会执行修改后的迁移,历史模型状态也会依据修改后的文件构建。如果修改了字段默认值、operations、dependencies 或 RunPython 的业务逻辑,就可能造成新旧环境行为不一致。
这次应把改动限制在导入兼容性上,并将修改提交到版本控制,随代码统一部署。修复本身不需要回滚重跑旧迁移,也不需要使用 --fake。
为什么不优先使用 monkey patch?
下面的补丁可以让旧导入重新成功:
from datetime import timezone
import django.utils.timezone as django_timezone
if not hasattr(django_timezone, "utc"):
django_timezone.utc = timezone.utc
不过,补丁必须在有问题的迁移被导入之前执行,而且只对当前 Python 进程生效。
把补丁放进 manage.py 和 wsgi.py,只能覆盖经过这些文件启动的流程。其他测试入口、直接调用管理命令的脚本或独立进程,仍可能遇到同样的问题。即使补丁暂时有效,也不能说明所有生产入口都已覆盖。
如果短期内无法修改相关迁移,可以将补丁作为过渡措施,集中管理并验证加载时机。对于自己维护的迁移,直接修复失效导入更容易长期维护。
squashmigrations 可用于后续整理历史,但压缩迁移需要分阶段发布,并处理旧文件及依赖。只要失效文件仍在加载范围内,导入错误就仍需解决。Django 迁移压缩说明
修复后如何验证?
先移除临时 monkey patch,确认验证过程使用的是修复后的迁移文件,再运行:
python manage.py showmigrations
python manage.py makemigrations --check --dry-run
python manage.py migrate --plan
检查迁移是否能够加载、已应用状态是否符合预期,以及是否出现意外的待执行操作。如果 makemigrations --check --dry-run 报告模型变化,需要另行判断这些变化是否属于升级带来的真实差异。
随后,在独立测试数据库中执行:
python manage.py migrate
最好同时验证两种路径:已有数据库的升级,以及空数据库从头执行全部迁移。前者检查升级是否受影响,后者检查历史迁移能否在新环境中完整重放。如果包含时间处理的数据迁移,还应检查相关时间值和处理结果。
备注:内容仅供参考。
