Django中DateTimeField改为DateField后查询历史数据报错如何解决?
问题触发原因
这个问题本质是SQLite弱类型特性+Django默认迁移逻辑不完善共同导致的:
- 你原来的
start_date、end_date是DateTimeField,SQLite中存储的是完整的带时间的字符串格式,比如2021-11-30 11:41:29 - 直接执行
AlterField把字段类型改成DateField时,Django只会修改表结构的字段类型声明,不会主动转换已有数据的存储格式,原有带时间的字符串不会被截断为纯日期格式 - 当查询数据时,Django会按
DateField的规则解析值,用-分割字符串后取三段转整数生成日期对象,此时最后一段会变成31 11:41:29,转int时报错。
如果是MySQL、PostgreSQL这类强类型数据库,改字段类型时会自动截断时间部分保留日期,不会出现这个问题,仅SQLite场景会触发。
修复方案
分两种场景处理:
场景1:错误迁移还未在生产环境执行
先回退本地迁移、删除错误的迁移文件,按三步安全修改字段类型:
- 给
FacilityTender模型新增两个临时DateField字段,设置null=True:
start_date_new = models.DateField(null=True) end_date_new = models.DateField(null=True)
- 生成迁移并执行,再新建一个数据迁移,把原有
DateTimeField的日期部分赋值给新字段:
# 数据迁移的operations里添加 migrations.RunPython(lambda apps, schema_editor: ( FacilityTender := apps.get_model('company', 'FacilityTender'), [setattr(obj, 'start_date_new', obj.start_date.date()) or setattr(obj, 'end_date_new', obj.end_date.date()) or obj.save() for obj in FacilityTender.objects.all()] ))
- 删除原有
start_date、end_date字段,把start_date_new、end_date_new重命名为start_date、end_date,再生成迁移执行即可。
场景2:错误迁移已经在生产环境执行
直接写SQL修正现有存储的字段值,新建一个空白迁移,添加如下操作:
migrations.RunSQL( "UPDATE company_facilitytender SET start_date = DATE(start_date), end_date = DATE(end_date);" )
执行迁移后,所有带时间的字符串会被转换为纯日期格式,报错即可消失。
内容的提问来源于stack exchange,提问作者JSRB
相关产品推荐
相关产品推荐

