Django新增带随机默认函数字段如何为旧对象生成唯一值?
问题原因
Django执行新增字段的迁移操作时,你传入的default回调函数只会在迁移启动时被调用1次,返回的同一个值会被批量赋值给所有已存在的旧数据行。只有后续新创建的对象,才会每次新增时调用default生成新值,这就是你所有旧数据字段值重复的原因。
解决步骤
总共需要执行3次迁移完成需求:
- 第一步:先将新增字段设置为允许空值,避免迁移时直接赋值
修改models.py中字段定义:
class Subscription(models.Model): # ... 其他字段省略 ... uniqueSubscriptionId = models.CharField(max_length=30, null=True)
执行迁移命令生成第一个迁移文件:python manage.py makemigrations
执行迁移:python manage.py migrate
此时表中uniqueSubscriptionId字段已新增,所有旧数据该字段值为NULL。
- 第二步:创建自定义迁移文件,为所有旧数据逐个生成唯一值
执行命令生成空迁移文件(将命令中的你的app名替换为你自己的Django app名称):python manage.py makemigrations 你的app名 --empty
打开新生成的空迁移文件,修改内容如下:
from django.db import migrations from django.utils.crypto import get_random_string def generate_unique_ids(apps, schema_editor): # 注意此处不能直接导入你的Subscription模型,必须用apps.get_model获取迁移时的模型版本 Subscription = apps.get_model('你的app名', 'Subscription') # 遍历所有旧数据,逐个生成唯一值并保存 for sub in Subscription.objects.all(): sub.uniqueSubscriptionId = get_random_string(20) sub.save() class Migration(migrations.Migration): dependencies = [ # 这里是自动生成的依赖,不用修改 ('你的app名', '00xx_xxxxxx'), ] operations = [ migrations.RunPython(generate_unique_ids), ]
保存后执行迁移:python manage.py migrate
此时所有旧数据的uniqueSubscriptionId字段已经被赋值为唯一随机值。
- 第三步:将字段改回非空,设置默认值适配后续新增数据
再次修改models.py中字段定义,恢复你原本的配置:
class Subscription(models.Model): # ... 其他字段省略 ... uniqueSubscriptionId = models.CharField(default=generateUniqueSubscription, max_length=30)
执行迁移命令生成第三个迁移文件,执行时如果弹出询问框,选择「为现有行设置一次性默认值」即可,此时字段会被改为非空,后续新增的对象会自动调用generateUniqueSubscription生成唯一值:python manage.py makemigrationspython manage.py migrate
额外说明
如果你的表数据量非常大,遍历所有对象保存的操作会比较慢,可以改用Django的批量更新逻辑优化速度;如果该字段需要加唯一约束unique=True,请在第二步完成旧数据赋值之后,再在第三步修改字段时添加约束,避免迁移时报重复值错误。
内容的提问来源于stack exchange,提问作者Pranava Mohan
相关产品推荐
相关产品推荐

