[Django]-Django UUIDField default value

6👍

You have lot of problems in here, there are 3 steps to achieve your goal.

  1. If you have data already in your model you have to define it as null=True and remove the default=uuid.uuid4. Why? Because if you put a default value, it will try to add the same default address to all the models when you run your migration so it will of course give you the error django.db.utils.IntegrityError: (1062, "Duplicate entry '3ba46cb5f7f340ffa43e348cb789901a' for key 'carbon.uuid'") because use the same uuid for each row.

  2. Create a migration to update your existing rows in the model and put them a uuid as following:
    phython src/manage.py makemigrations app --name set_uuid_mymodel --empty

# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations
import uuid

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

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

Look at the official documentation: https://docs.djangoproject.com/en/3.0/howto/writing-migrations/#migrations-that-add-unique-fields

Then you can manage new rows generation uuid in the save method of the model:

def save(self, *args, **kwargs):
    ''' On save, update uuid and ovoid clash with existing code '''
    if not self.uuid:
        self.uuid = uuid.uuid4()
    return super(YourModel, self).save(*args, **kwargs)

3👍

To add a new required field (null=False) for a model, you should include a default value. This value will be applied to all existing records.

For UUIDField, during migrations, Django does not create a new UUID value for each record. A single value is generated through uuid.uuid4() and applied to all records using a single SQL. So, knowing that unique=True is applied at database-level, if you have existing records on your database, during migration an IntegrityError will be raised for this reason.

If you need the existing records with a valid uuid value (you cannot recreate the entire model migration with the uuid field or just drop the existing records before run the migration) is possible force a new value for each record using a RunPython operation inside your migration.

Edit your migration to do the following steps:

  1. Add a nullable uuid field, without unique and default settings, so Django can create the column without integrity errors.
  2. Add a custom Python code to force the uuid for existing records
  3. Alter the uuid field, recovering the original settings
import uuid

from django.db import migrations


def force_uuid(apps, schema_editor):
    # We get the model from the versioned app registry;
    # if we directly import it, it'll be the wrong version
    MyModel = apps.get_model('myapp', 'MyModel')
    db_alias = schema_editor.connection.alias

    for instance in MyModel.objects.using(db_alias).all():
        instance.uuid = uuid.uuid4()
        instance.save()


class Migration(migrations.Migration):
    dependencies = []
    operations = [
        # Pre generate the uuid field (nullable)
        migrations.AddField(
            model_name='mymodel',
            name='uuid',
            field=models.UUIDField(null=True, unique=False, editable=False),
        ),
        # Force uuid on existing records
        migrations.RunPython(force_uuid),
        # Recovering original settings
        migrations.AlterField(
            model_name='mymodel',
            name='uuid',
            field=models.UUIDField(
                default=uuid.uuid4, null=False, unique=True, editable=False
            ),
        ),
    ]


Notes

  • If you have a lot of records to generate a unique uuid, you can benefit from bulk_create() instead loop over all records and adds a lot of single inserts

Leave a comment