Django迁移Postgres报错:无法将无时区time转换为带时区timestamp
问题根本原因
SQLite属于弱类型数据库,对字段类型的校验非常宽松,即使你把仅含时分秒的time类型数据存入DateTimeField对应的列,也不会触发报错。而PostgreSQL是强类型数据库,严格区分两种数据类型:
time without time zone:仅存储时分秒的时间类型timestamp with time zone:存储年月日+时分秒+时区信息的日期时间类型
当你把项目切换到PostgreSQL运行时,不管是执行迁移还是写入数据,只要出现把time类型值写入DateTimeField对应列的场景,就会触发类型转换失败的报错。
解决方案
分两种场景处理:
场景1:执行migrate迁移命令时报错
这种情况一般是你之前修改过字段类型(比如最初把start_time定义为TimeField,后来改成了DateTimeField),旧迁移文件里的字段类型变更语句在PostgreSQL下无法自动执行。
- 如果是新项目,生产数据库还没有有效业务数据:
- 删除
api应用下migrations文件夹中除了__init__.py之外的所有文件 - 重新执行命令生成新的迁移文件:
python manage.py makemigrations - 执行迁移:
python manage.py migrate
新生成的迁移文件会直接创建正确的timestamp with time zone类型列,不会触发类型转换问题。
- 删除
- 如果生产数据库已有数据,不能清空迁移记录:
找到触发报错的迁移文件,将原有的字段修改操作替换为自定义SQL语句,手动指定类型转换规则,示例代码如下:from django.db import migrations class Migration(migrations.Migration): dependencies = [ ('api', '000x_previous_migration'), ] operations = [ migrations.RunSQL( # 正向迁移:将time类型转为timestamp类型 sql=""" ALTER TABLE api_tasks ALTER COLUMN start_time TYPE timestamp with time zone USING (current_date + start_time)::timestamp with time zone; ALTER TABLE api_tasks ALTER COLUMN end_time TYPE timestamp with time zone USING (current_date + end_time)::timestamp with time zone; """, # 回滚操作:将timestamp转回time类型 reverse_sql=""" ALTER TABLE api_tasks ALTER COLUMN start_time TYPE time without time zone USING start_time::time; ALTER TABLE api_tasks ALTER COLUMN end_time TYPE time without time zone USING end_time::time; """ ) ]
场景2:运行时写入/更新数据时报错
这种情况是你的业务代码中给start_time、end_time字段赋值时,传入了time类型的值,而非datetime类型的值。
排查你的视图、序列化器、定时任务等所有操作Tasks模型的代码,确保给DateTimeField类型字段赋值的是datetime类型对象,不要仅传入时分秒的time类型对象。
校验说明
你的settings.py中USE_TZ = True、TIME_ZONE配置正确,不需要修改时区相关配置,只要确保数据类型匹配即可正常运行。
内容的提问来源于stack exchange,提问作者Sanjay Kapilesh
相关产品推荐
相关产品推荐

