Django模型新增字段后,如何区分新旧对象的默认值?
这个问题我之前也碰到过,核心原因是Django在生成迁移文件时,会自动给现有数据库记录填充字段的default值——哪怕你设置了null=True,因为迁移逻辑默认会确保现有行有值,不会留空。下面分两种场景给你解决方案:
场景一:还没执行migrate命令(仅生成了迁移文件)
这是最容易处理的情况,按以下步骤来:
先修改模型字段,暂时去掉default:
class Some(Model): some_value = CharField(max_length=20) created = FloatField(null=True, blank=True) # 只保留null和blank,去掉default重新生成并执行迁移:
先删掉之前生成的迁移文件(如果有的话),然后运行:python manage.py makemigrations python manage.py migrate这时候旧对象的
created字段会被设为NULL,新对象暂时不会自动填充值。添加default并再次生成迁移:
修改模型字段,加上default=time.time:class Some(Model): some_value = CharField(max_length=20) created = FloatField(null=True, blank=True, default=time.time)再次生成并执行迁移:
python manage.py makemigrations python manage.py migrate这次迁移只会给后续新创建的对象自动填充当前时间戳,旧对象的
created依然是NULL,完美符合你的需求。
场景二:已经执行了migrate,旧对象已被填充错误的时间值
如果已经把错误的默认值写到数据库里了,那就需要写一个数据迁移来修正:
生成空的迁移文件:
python manage.py makemigrations --empty your_app_name替换
your_app_name为你的应用名称。编辑迁移文件,添加数据修正逻辑:
打开生成的空迁移文件,修改成类似这样:from django.db import migrations, models import time def set_old_created_to_none(apps, schema_editor): # 获取历史版本的模型(不要直接导入当前模型,避免后续修改影响) Some = apps.get_model('your_app_name', 'Some') # 将所有旧对象的created字段设为None Some.objects.all().update(created=None) # 如果想给旧对象设置指定时间,比如某个固定时间戳,改成: # Some.objects.all().update(created=1620000000) class Migration(migrations.Migration): dependencies = [ # 这里会自动生成依赖的上一个迁移,不要修改 ('your_app_name', 'xxxxxx_prev_migration'), ] operations = [ migrations.RunPython(set_old_created_to_none), ]执行迁移:
python manage.py migrate执行后,旧对象的
created就会被改成None(或你指定的时间),新对象依然会用time.time作为默认值。
额外提示:为什么null=True没用?
当你添加带default的字段时,Django生成的迁移文件里会包含给现有记录设置default值的逻辑,优先级高于null=True——因为Django默认假设你希望现有数据符合新字段的约束,所以会自动填充默认值,而不是留空。只有当你明确告诉迁移逻辑不要填充默认值(比如第一步里去掉default),才会让旧对象的字段为NULL。
内容的提问来源于stack exchange,提问作者Ron Moran

