2016-02-08 49 views
15

我有一個uuid字段(不是主鍵)。生成的遷移是:Django遷移與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), 
     ), 
     ... 
    ] 

但是這樣做python manage.py migrate當它與崩潰:

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

奇怪的是,似乎問題不與主鍵(這是可能通過數據庫中創建的發生,而不是由Django的內部?)

如何添加一個uuid字段,並確保遷移工作?

+3

該文檔解釋這很好,見[即添加獨特的領域遷移(https://docs.djangoproject.com/en/1.9/howto/writing-migrations/#migrations-that-add-unique -fields)。 – knbk

+0

@knbk:謝謝。這是驚人的。驚人的有趣和驚人的複雜。只是..要..生成.. uuids – dangonfast

+0

或者,它是一個黑客,但提供了類似的功能,並且更簡單:str_uuid = models.CharField(max_length = 36,default = lambda:str(uuid.uuid4()))。遷移後,爲模型中的每個對象運行一個循環,並保存()以獲得唯一的uuid。你不需要一個唯一的索引,因爲相同的uuid4的機率幾乎爲零。 –

回答

1

在該模式下,您已配置,您需要uuid字段的唯一值,但使用默認值(全部相同)。因此,如果數據庫中有兩個「設備」對象,則遷移會使用默認的「uuid.uuid4」值向其添加「uuid」字段,並且當它嘗試將其設置爲第二個時,它會因爲唯一的約束而崩潰。

如果你放棄你的數據庫並創建新的對象可能沒有問題,但那不是解決方案顯然是生產數據庫:D。

更好的解決方案是創建一個數據遷移,它爲數據庫中的每個現有對象設置不同的uuid值(由默認'uuid'庫生成)。你可以在這裏閱讀更多關於數據遷移的地方: https://docs.djangoproject.com/en/1.10/topics/migrations/#data-migrations

然後,當你創建新的對象時,django會自動生成不同的uuid。 ;)

對於主鍵:Django默認將它添加到模型中。

7

(從第一個評論採取應答)

見Django文檔 - Migrations that add unique fields

他們建議改變你的單身遷移成三個獨立的遷移:

  1. 創建領域,設置爲空,但不是唯一的
  2. 生成唯一的UUID
  3. 將字段更改爲唯一
10

下面是一個示例,通過一次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) 
     ) 
    ]