使用uuid字段的Django迁移会生成重复值

dan*_*ast 37 python django uuid django-migrations

我有一个uuid字段(不是主键).生成的迁移是:

from __future__ import unicode_literals

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):

    dependencies = [
        ....
    ]

    operations = [
        ...
        migrations.AddField(
            model_name='device',
            name='uuid',
            field=models.UUIDField(default=uuid.uuid4, unique=True),
        ),
        ...
    ]
Run Code Online (Sandbox Code Playgroud)

但是当python manage.py migrate它做的时候崩溃:

django.db.utils.IntegrityError:无法创建唯一索引"restaurants_device_uuid_key"DETAIL:Key(uuid)=(f3858ded-b8e0-4ac0-8436-8a61b10efc73)重复.

奇怪的是,主键(它可能是由数据库创建的,而不是由django内部创建的)似乎不会出现问题.

如何添加uuid字段,并确保迁移有效?

v.t*_*rey 38

这是一个通过RunPython调用在一次迁移中完成所有操作的示例.

# -*- coding: utf-8 -*
from __future__ import unicode_literals

from django.db import migrations, models
import uuid


def create_uuid(apps, schema_editor):
    Device = apps.get_model('device_app', 'Device')
    for device in Device.objects.all():
        device.uuid = uuid.uuid4()
        device.save()


class Migration(migrations.Migration):

    dependencies = [
        ('device_app', 'XXXX'),
    ]

    operations = [
        migrations.AddField(
            model_name='device',
            name='uuid',
            field=models.UUIDField(blank=True, null=True),
        ),
        migrations.RunPython(create_uuid),
        migrations.AlterField(
            model_name='device',
            name='uuid',
            field=models.UUIDField(unique=True)
        )
    ]
Run Code Online (Sandbox Code Playgroud)

  • 但是这里没有比赛条件吗?如果在运行create_uuid时对用户进行了编辑,则其数据将被覆盖(允许,Django尝试锁定表以防止这种情况)。而是应为device.save(update_fields = ['uuid']),如Django文档中所述。 (3认同)

Ale*_*x L 21

(答案取自第一条评论)

请参阅django文档 - 添加唯一字段的迁移

他们建议将单个迁移更改为三个单独的迁移:

  1. 创建字段,设置为null但不唯一
  2. 生成唯一的UUID
  3. 改变这个领域是独一无二的

  • 此外,他们的文档似乎不准确。他们在第一次迁移中将该字段设置为唯一,这是不正确的。您正确地指出,它应该首先设置为 null,然后创建,然后填充,然后设置为 unique。 (3认同)

小智 3

在您已配置的模式中,您希望 uuid 字段具有唯一值,但具有默认值(所有值都相同)。因此,如果数据库中有两个“设备”对象,迁移会使用默认的“uuid.uuid4”值向它们添加“uuid”字段,当它尝试将其设置为第二个对象时,由于唯一的约束,它会崩溃。

如果您删除数据库并创建新对象,可能不会有问题,但这显然不是生产数据库的解决方案:D。

更好的解决方案是创建一个数据迁移,为数据库中的每个现有对象设置不同的 uuid 值(由默认的“uuid”库生成)。您可以在此处阅读有关数据迁移的更多信息: https ://docs.djangoproject.com/en/1.10/topics/migrations/#data-migrations

然后,当你创建新对象时,django会自动生成不同的uuid。;)

对于主键:Django 默认将其添加到模型中。