在现有的django模型中添加新的唯一字段的最佳做法

时间:2019-07-08 12:19:16

标签: python django python-3.x database-migration django-migrations

我有一个现有的模型,看起来类似于以下内容...

class Resource(models.Model):

    id = models.AutoField(primary_key=True)

我们已经使用了一段时间,现在在我们的数据库中有约100万个这些Resource对象的实例(以及相关的ForeignKey / else用法)。

我现在需要跟踪此模型上的另一个ID,我要强制执行的ID是唯一的。

other_id = models.IntegerField(unique=True)

other_id信息当前存储在某些外部CSV中,我想(在过程中的某个时刻)将该信息加载到所有现有的Resource实例中。

添加以上字段后,Django的makemigrations可以正常工作。但是,当我对现有数据库应用上述迁移时,出现错误,表明我需要提供默认值以用于所有现有Resource实例。我敢肯定你们中的许多人都看过类似的东西。

克服此限制的最佳方法是什么?我想到的一些方法...

    • 删除unique=True要求
    • 应用迁移
    • (通过某些管理命令或1-off脚本)将other_id值外部加载到所有现有模型中
    • 重新添加unique=True并应用迁移
    • 将所有现有数据转储到JSON
    • 刷新所有表
    • 应用迁移(使用unique = True)
    • 编写一个脚本,以重新加载数据,并添加正确的other_id
  1. (不确定是否可行)-编写一些自定义迁移逻辑,以在运行other_id时自动引用这些外部CSV来加载manage.py migrate值。如果(在将来的某个时刻)有人重新运行这些迁移而这部分失败(无法在CSV中找到现有资源id来提取other_id),则可能会遇到问题。

所有这些感觉都很复杂,但是我想我想做的也不是最简单的事情。

有什么想法吗?我必须想象过去有人不得不解决类似的问题。

谢谢!

2 个答案:

答案 0 :(得分:2)

实际上,源或问题本身并不是唯一的约束,而是您的字段不允许为null且没有默认值的事实-对于非唯一字段,您将遇到同样的错误。< / p>

此处的正确解决方案是允许该字段为空(null=True),并将其默认设置为None(它将转换为sql“ null”)。由于null值被排除在唯一约束之外(至少在数据库供应商遵守SQL标准的情况下),因此,这使您可以应用架构更改,同时仍可以确保非空值不能重复。

然后,您可能希望进行数据迁移以加载已知的“ other_id”值,并最终进行第三次架构迁移以不允许该字段为空值-仅当且仅当您知道已为所有记录填充此字段时。

答案 1 :(得分:1)

Django有一个名为Data Migrations的东西,您可以在其中创建一个迁移文件,该文件在应用迁移时会修改/删除/向数据库添加数据。

在这种情况下,您将创建3个不同的迁移:

  1. 创建一个允许使用null=True使用空值的迁移。
  2. 创建用于填充数据的数据迁移。
  3. 通过删除在步骤1中添加的null=True,创建禁止空值的迁移。

然后,您运行python manage.py migrate时,它将以正确的顺序应用步骤1-3中的所有迁移。

您的数据迁移看起来像这样:

from django.db import migrations

def populate_reference(apps, schema_editor):
    MyModel = apps.get_model('yourappname', 'MyModel')
    for obj in MyModel.objects.all():
        obj.other_id = random_id_generator()
        obj.save()

class Migration(migrations.Migration):

    dependencies = [
        ('yourappname', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(populate_reference),
    ]

您可以使用./manage.py makemigrations --empty yourappname命令创建一个空的迁移文件。