3

I am trying to add a UUID field to an existing table. I specified that default = uuid.uuid4 however, it Django doesn't seem to call uuid.uuid4 function for every row. So when I migrate I keep getting duplicated uuid error.

My Django version is 1.8.2.

from django.db import models, migrations
import uuid


class Migration(migrations.Migration):

    dependencies = [
        ('conv', '0008_video_video_uri'),
    ]

    operations = [
        migrations.AddField(
            model_name='conversation',
            name='channel_id',
            field=models.UUIDField(unique=True, default=uuid.uuid4, editable=False),
        ),
    ]

Below the error:

> >  File "/home/yonk/projects/trailerapp/venv/local/lib/python2.7/site-packages/django/db/backends/utils.py",
> line 64, in execute
>     return self.cursor.execute(sql, params) django.db.utils.IntegrityError: could not create unique index
> "conv_conversation_channel_id_68f7d58df7c78d61_uniq" DETAIL:  Key
> (channel_id)=(5f512cbe-e514-4bf5-bf5a-3efd1a94e401) is duplicated.
Roger Oliveira
  • 1,589
  • 1
  • 27
  • 55
Bonk
  • 1,859
  • 9
  • 28
  • 46

2 Answers2

4

Here you have django docs describing exactly what you want: https://docs.djangoproject.com/en/1.8/howto/writing-migrations/#migrations-that-add-unique-fields

You will need two migration files.

  1. First one Adds fields, also change unique=True to null=True so django won't try to use default value...
  2. Second migration populates the field.

So second migration should look like this:

def gen_uuid(apps, schema_editor):
    MyModel = apps.get_model('myapp', 'MyModel')
    for row in MyModel.objects.all():
        row.uuid = uuid.uuid4()
        row.save()


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0004_add_uuid_field'),
    ]

    operations = [
        # omit reverse_code=... if you don't want the migration to be reversible.
        migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
    ]
Visgean Skeloru
  • 2,237
  • 1
  • 24
  • 33
  • That's exactly what I did, but I still run into conflict uuid – Bonk Jun 19 '15 at 00:18
  • 1
    No it isn't, at least not from the code you pasted, you don't have any equivalant of 0005-populate_uuid_values.py migration, you will need two migration files. In first you alter the field and in second you populate the field. You did only the first step. Also the docs are mispelled, first snippet should be 004 and not 006... – Visgean Skeloru Jun 19 '15 at 00:23
  • Also you did not "Change unique=True to null=True – this will create the intermediary null field and defer creating the unique constraint until we’ve populated unique values on all the rows." – Visgean Skeloru Jun 19 '15 at 00:25
  • My mistake, I didn't read the whole thing. Problem has been resolved – Bonk Jun 19 '15 at 00:36
1

In order to get a new value each time, you will need to set the default to a callable, otherwise as you have observed, uuid.uuid4 will be calculated once and then that single value will be used each time.

This other StackOverflow question and answer shows how to pass a callable as the default value.

EDIT: This answer only applies to versions of Django 1.7 and lower.

Community
  • 1
  • 1
Aurora
  • 4,384
  • 3
  • 34
  • 44
  • 1
    Lambda functions are no longer recommended for django 1.7+, instead use `callable` instead of `callable()`. I used the exact code as described here https://docs.djangoproject.com/en/1.8/ref/models/fields/#uuidfield – Bonk Jun 19 '15 at 00:01