当已经有 ID 字段时如何使 UUID 字段成为默认值

Moh*_*ani 6 django django-models python-3.x django-rest-framework

我正在做一个项目,问题是我已经用简单的 ID 列创建了模型,但现在我的要求发生了变化,我想用 UUID 字段替换 ID 字段(模型)我刚刚更新了我的模型:

uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)
Run Code Online (Sandbox Code Playgroud)

但是当我运行迁移时出现错误

django.db.utils.OperationalError: (1829, “不能删除列 'id':需要在外键约束中

请指导我如何执行此迁移?

ali*_*iva 14

这是您需要执行的操作列表

1 - 将新uuid字段添加到模型(我命名此模型Base)然后生成迁移文件

uuid = models.UUIDField(默认=uuid4,空白=真,空=真)

  • 请注意,uuid 还不是主键
  • 注意 blank = null =True

2- 在此步骤中,您应该uuid使用有效数据填充字段。您应该为Base模型编写数据迁移文件。请查看文档以获取更多信息

你的 forwards 方法应该是这样的:

for item in Base.objects.all():
    item.uuid = uuid4()
    item.save()
Run Code Online (Sandbox Code Playgroud)

3- 将 uuid 字段更改为此并生成迁移

uuid = models.UUIDField(default=uuid4, unique=True)

  • 请注意,uuid 还不是主键,但它现在是唯一的

4 - 对于指向Base模型的其他模型,您应该添加一个指向 uuid 字段的新外键

假设您与 Base 模型的默认关系是这样的

base = models.ForeignKey(
    Base, on_delete=models.PROTECT, related_name='base'
)
Run Code Online (Sandbox Code Playgroud)

你应该像这样添加一个临时字段

base_uuid = models.ForeignKey(
    Base,
    on_delete=models.PROTECT,
    related_name='base_uuid',
    to_field='uuid',
    blank=True,
    null=True,
)
Run Code Online (Sandbox Code Playgroud)
  • 请注意,我已经明确定义了to_field哪个告诉 django 这个外键没有指向默认pk字段
  • 你应该保留旧的外键字段
  • 还设置 blank = null = True
  • 对于指向Base您的所有外键/多多字段,您应该添加此临时字段

5- 在这一步中,您应该创建一个数据迁移文件,

在此数据迁移文件中,您需要base_uuid使用有效数据(基于旧base字段)填充所有字段,您的迁移代码可以是这样的

for item in RelatedModel.objects.all():
    item.base_uuid_id = item.base.uuid
Run Code Online (Sandbox Code Playgroud)
  • 在此步骤之后base_uuid,所有模型中的所有字段都应包含有效数据

6-在所有相关模型中删除相关字段(保留新base_uuid字段但丢弃旧相关字段)并生成迁移文件

7- 删除所有 FK 字段上的 db_constraint - 这是必需的,因为 django 将通过唯一约束进行连接base_uuid,如果我们更改uuid为 pk ,将从中删除该约束。

for item in Base.objects.all():
    item.uuid = uuid4()
    item.save()
Run Code Online (Sandbox Code Playgroud)

8-在Base模型中更改uuid字段并生成迁移文件

uuid = models.UUIDField(默认=uuid4,primary_key=True)

  • uuid 现在是主键!

9- 在所有相关模型(您添加base_uuid字段的模型)中更新字段

  • 重命名字段名称(_uuid从列名称中删除)(使其成为单独的迁移!)
  • 更改relate_name字段名称(_uuid从相关名称中删除)
  • blank = null = true必要时删除
  • 删除to_field='uuid'参数(你不再需要它了)

非常重要的注意事项:在对实际数据运行此代码之前,使用测试数据运行这些代码并从数据库创建完整备份

  • 很好的答案@aliva,但是,我在第 7 步遇到错误:django.db.utils.ProgrammingError: 无法将类型 uuid 转换为整数。我确信我正确地完成了所有步骤。我缺少什么?你知道如何解决这个问题吗?谢谢。 (4认同)