2016-02-08 35 views
15

Ho un campo uuid (non una chiave primaria). La migrazione generato è:La migrazione di Django con campo uuid genera valori duplicati

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

Ma quando si fa python manage.py migrate si sta arrestandosi con:

django.db.utils.IntegrityError: could not create unique index "restaurants_device_uuid_key" DETAIL: Key (uuid)=(f3858ded-b8e0-4ac0-8436-8a61b10efc73) is duplicated.

Stranamente, il problema non sembra verificarsi con chiavi primarie (che sono forse creati dal database, e non internamente da django?)

Come posso aggiungere un campo uuid e assicurarmi che le migrazioni funzionino?

+3

La documentazione spiegano questo abbastanza bene, vedere [Migrazioni che si aggiungono campi univoci] (https://docs.djangoproject.com/en/1.9/howto/writing-migrations/#migrations-that-add-unique -campi). – knbk

+0

@knbk: Grazie. È fantastico. Amaizingly interessante e incredibilmente complesso. Semplicemente .. per .. generare .. uuids – dangonfast

+0

In alternativa, è un hack ma fornisce una funzionalità simile, e molto più semplice: str_uuid = models.CharField (max_length = 36, default = lambda: str (uuid.uuid4())). Dopo la migrazione, esegui un ciclo per ciascun oggetto nel modello e salva() per ottenere un uuid univoco. Non hai bisogno di un indice univoco perché le probabilità dello stesso uuid4 sono vicine allo zero. –

risposta

1

Nella modalità, è stato configurato, che si desidera valori univoci per i campi uuid, ma con valori predefiniti (lo stesso per tutti). Quindi, se nel database sono presenti due oggetti "dispositivo", le migrazioni aggiungono il campo 'uuid' a loro con il valore predefinito 'uuid.uuid4' e quando tenta di impostarlo sul secondo, si blocca a causa dei vincoli univoci .

Se si rilascia il db e si creano nuovi oggetti probabilmente non ci saranno problemi ma questa non è una soluzione per la produzione db ovviamente: D.

Una soluzione migliore consiste nel creare una migrazione dei dati che imposta un valore uuid diverso (generato dalla libreria 'uuid' predefinita) su ogni oggetto esistente nel database. Puoi leggere ulteriori informazioni sulla migrazione dei dati qui: https://docs.djangoproject.com/en/1.10/topics/migrations/#data-migrations

Quindi, quando crei nuovi oggetti, django genererà automaticamente uuid diversi. ;)

Per le chiavi primarie: Django lo aggiunge al modello per impostazione predefinita.

7

(Risposta tratto dal primo commento)

Vedere la documentazione Django - Migrations that add unique fields

Essi raccomandano cambiare la migrazione unico in tre migrazioni distinte:

  1. Crea campo, impostare su null, ma non univoco
  2. Genera UUID univoci
  3. Modificare il campo in modo univoco
10

Ecco un esempio che esegue tutto in un'unica migrazione grazie a una chiamata 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) 
     ) 
    ]