EDIT 3-10-24: As u/heppy
pointed out on Reddit, there was an error with my initial implementation. I deleted the author's id before populating the book's new author foreign key, meaning that book's reference to author id no longer held any meaning. I've since fixed this. See the original comment here.
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:
Add
uuid
field toAuthor
while preserving theid
field as an integerPopulate
uuid
in the migration per https://docs.djangoproject.com/en/5.0/howto/writing-migrations/#migrations-that-add-unique-fieldsAlter
Book
'sauthor
foreign key reference toAuthor
to be an integerMake
uuid
theAuthor
's primary keyRename
Book
'sauthor
toauthor_old
Add a new
author
foreign key field toBook
Create new empty migration
python
manage.py
makemigrations --empty library
Add a
RunPython
step to empty migration that populatesauthor
fromauthor_old
for each book rowRemove
Author'sid
Rename
Author
'suuid
toid
Drop
author_old
Make the
Book
'sauthor
null againSquash the new migrations (https://docs.djangoproject.com/en/5.0/topics/migrations/#migration-squashing). This ensures that the database is not in a weird state if only half of the migrations get applied.
python
manage.py
squashmigrations library 0002 0012
Follow the steps stated in the command output and the migration file to copy the custom
populate_*
functions into the squashed migrationDelete old migrations that the squashed migration replaces
Rename the squashed migration
Bonus points for using ulid
(https://www.bitecode.dev/p/python-libs-that-i-wish-were-part) instead of the standard uuid.uuid4
!
See the git commit history of my example repository, which can be found at https://github.com/thuibr/django-uuid-migration-example. This is a good way to see the result of each step.