Django : How to add migration for unique keys

- 5 mins

This post is about adding migration for a field which should have unique values.

Lets us consider a UserProfile model.

# models.py

from django.db import models
from django.contrib.auth.models import User


class UserProfile(models.Model):

    user = models.OneToOneField(User,
                                primary_key=True,
                                related_name='profile',
                                on_delete=models.CASCADE)
    contact_number = models.CharField(max_length=10, null=True, blank=True)

    class Meta:
        db_table = 'user_profile'

    def __str__(self):
        return self.user.username

Now suppose that, we need to add a field which should have unique values. Lets consider that we want to add a new column called user_id_custom which is a UUIDField and should have unique values of UUIDField. The most simple piece of code to accomplish this would look like

# profiles/models.py
import uuid

from django.db import models
from django.contrib.auth.models import User


class UserProfile(models.Model):

    user = models.OneToOneField(User,
                                primary_key=True,
                                related_name='profile',
                                on_delete=models.CASCADE)
    contact_number = models.CharField(max_length=10, null=True, blank=True)
    # this is new field
    user_id_custom = models.UUIDField(default=uuid.uuid4, unique=True)

    class Meta:
        db_table = 'user_profile'

    def __str__(self):
        return self.user.username

Now lets create a migration file for this. We generally prefer named migration(max length is 50), so that it becomes easy to track later what the migration was about.

python manage.py makemigrations profiles --name=add_unique_user_id_custom

Here profiles is the name of the app. It can be any value according to your usecase. The migration is generated successfully. It can be found in

tree profiles

profiles
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│   ├── 0001_initial.py
│   ├── 0002_add_unique_user_id_custom.py
│   ├── __init__.py
├── models.py
├── tests.py
└── views.py

Now lets run migrate to apply changes to the database.

python manage.py migrate

Now when the migration is applied, we get an error. This error may vary, but the error is an IntegrityError.

django.db.utils.IntegrityError: (1062, "Duplicate entry '3c4e98944d1f4474ab50917496124f4b' for key 'user_id_custom'")

One way to overcome this problem can be the following steps

The above listed is a manual process where in two migration files will be generated and a custom code(data migration) needs to be written to store unique uuids. This approach can be opted, but since django migration allows us to run custom python code using RunPython, second way can be an automated way of doing this. The second way will follow the same set of sequence, but only a single migration file will be generated.

In our case, we will be updating the generate migration file (here 0002_add_unique_user_id_custom.py) and adding some custom code to it.

# profiles/migrations/0002_add_unique_user_id_custom.py

from django.db import migrations, models
import uuid


def create_uuid(apps, schema_editor):
    UserProfile = apps.get_model('profiles', 'UserProfile')
    for user_profile in UserProfile.objects.all():
        user_profile.user_id_custom = uuid.uuid4()
        user_profile.save(update_fields=['user_id_custom'])


class Migration(migrations.Migration):

    dependencies = [
        ('profiles', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='userprofile',
            name='user_id_custom',
            field=models.UUIDField(blank=True, null=True, unique=True),
        ),
        migrations.RunPython(create_uuid),
        migrations.AlterField(
            model_name='userprofile',
            name='user_id_custom',
            field=models.UUIDField(unique=True, editable=False)
        )
    ]

Now we can apply the above migration by running the migrate command

python manage.py migrate

And this works perfectly.

Summary

This post is about how can we add a field with unique values to an existing django model. It adds some custom code in the generate migration file and skips detailing the manual way of creating step by step migration file. This post can be applied for any field which should have unique values.

References

Taranjeet Singh

Taranjeet Singh

Full Stack Developer, Entrepreneur

comments powered by Disqus
rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora