Update a Django Primary Key to UUID

This is a tricky operation, especially if other models have a foreign key to the target model. A short example:
from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

It is tricky because Book references an Author via id, which is currently an integer. When it changes to a uuid though, then Book will have an integer reference to a uuid field, and the database will raise errors. We work around this by downgrading the foreign key to a regular IntegerField and slowly massaging it to use the new uuid primary key of Book.

Steps

  1. Add uuid field to Author while preserving the id field as an integer

  2. Populate uuid in the migration per Migrations that Add Unique Fields

  3. Alter Book's author foreign key reference to Author to be an integer

  4. Make uuid the Author's primary key

  5. Rename Book's author to author_old

  6. Add a new author foreign key field to Book

  7. Create new empty migration

    1. python manage.py makemigrations --empty library
  8. Add a RunPython step to empty migration that populates author from author_old for each book row

  9. Remove Author's id

  10. Rename Author's uuid to id

  11. Drop author_old

  12. Make the Book's author null again

  13. Squash the new migrations. This ensures that the database is not in a weird state if only half of the migrations get applied.

    1. python manage.py squashmigrations library 0002 0012

    2. Follow the steps stated in the command output and the migration file to copy the custom populate_* functions into the squashed migration

    3. Delete old migrations that the squashed migration replaces

    4. Rename the squashed migration

Bonus points for using ulid instead of the standard uuid.uuid4!

See the git commit history in my example repository. This is a good way to see the result of each step.

← Back to blog