I managed to achieve this by creating three migrations. I started with the following model:
class MyModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
First, we need a migration to rename the primary key field and add a new id
placeholder IntegerField:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RenameField(
model_name='mymodel',
old_name='id',
new_name='uuid',
),
migrations.AddField(
model_name='mymodel',
name='new_id',
field=models.IntegerField(null=True),
),
]
Now in the next migration we need to backfill the id
IntegerField according to the order we want (I'll use the created_at
timestamp).
def backfill_pk(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
curr = 1
for m in MyModel.objects.all().order_by('created_at'):
m.new_id = curr
m.save()
curr += 1
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_rename_pk'),
]
operations = [
migrations.RunPython(backfill_pk, reverse_code=migrations.RunPython.noop),
]
And then finally we need to alter the uuid
and id
fields to their proper final configuration (note the order of operations below is important):
class Migration(migrations.Migration):
dependencies = [
('myapp', '0003_backfill_pk'),
]
operations = [
migrations.AlterField(
model_name='mymodel',
name='uuid',
field=models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, unique=True),
),
migrations.AlterField(
model_name='mymodel',
name='new_id',
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
migrations.RenameField(
model_name='mymodel',
old_name='new_id',
new_name='id',
),
]
The final model state will look like this (the id
field is implicit in Django):
class MyModel(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, db_index=True, editable=False, unique=True)
created_at = models.DateTimeField(auto_now_add=True)