web-dev-qa-db-fra.com

Django avec le champ uuid génère des valeurs dupliquées

J'ai un champ uuid (pas une clé primaire). La migration générée est:

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),
        ),
        ...
    ]

Mais en faisant python manage.py migrate il plante avec:

Django.db.utils.IntegrityError: impossible de créer un index unique "restaurants_device_uuid_key" DÉTAIL: La clé (uuid) = (f3858ded-b8e0-4ac0-8436-8a61b10efc73) est dupliquée.

Curieusement, le problème ne semble pas se produire avec les clés primaires (qui sont peut-être créées par la base de données, et non en interne par Django?)

Comment puis-je ajouter un champ uuid et m'assurer que les migrations fonctionnent?

35
dangonfast

Voici un exemple faisant tout en une seule migration grâce à un appel 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)
        )
    ]
34
v.thorey

(Réponse tirée du premier commentaire)

Voir la documentation Django - Migrations qui ajoutent des champs uniques

Ils recommandent de changer votre migration unique en trois migrations distinctes:

  1. Créer un champ, défini sur null mais pas unique
  2. Générer des UUID uniques
  3. Modifier le champ pour être unique
20
Alex L

Dans le mode, vous avez configuré que vous voulez des valeurs uniques pour les champs uuid, mais avec des valeurs par défaut (les mêmes pour tous). Donc, si vous avez deux objets 'device' dans la base de données, les migrations leur ajoutent le champ 'uuid' avec la valeur par défaut 'uuid.uuid4' et quand il essaie de le définir sur le second, il se bloque à cause des contraintes uniques .

Si vous déposez votre db et créez de nouveaux objets, il n'y aura probablement pas de problèmes mais ce n'est pas une solution pour la production de db évidemment: D.

Une meilleure solution consiste à créer une migration de données qui définit une valeur uuid différente (générée par la bibliothèque "uuid" par défaut) pour chaque objet existant dans la base de données. Vous pouvez en savoir plus sur les migrations de données ici: https://docs.djangoproject.com/en/1.10/topics/migrations/#data-migrations

Ensuite, lorsque vous créez de nouveaux objets, Django générera automatiquement différents uuid.;)

Pour les clés primaires: Django l'ajoute au modèle par défaut.

4
Ivo Donchev