Notes

Updating UUIDField on MariaDB to Django 5

December 27, 2023

Updating from Django 4 to Django 5 comes with an incompatible change to how Django UUIDFields are stored in MariaDB databases - in Django 4, Django would store UUIDFields as char(32) types but in Django 5, Django would store UUIDFields as uuid types. However, the Django 5 upgrade notes are incomplete. Following the directions on a nontrivial Django project, it’s still likely that you’ll get database errors like django.db.utils.OperationalError: (4078, "Cannot cast 'int' as 'uuid' in assignment of test_database.table_column.id") and errors from fitting 36-character UUID strings into 32-character database fields.

In order to fix this problem, I found that it’s better to convert char(32) MariaDB fields into uuid fields first before migrating to Django 5. That way, there are fewer changes during the update. In order to do so,

  1. While still on Django 4, replace django.models.UUIDField with a uuid field using a uuid database type with:

    # app/models.py
    
    from django.db import models
    
    
    class RealUUIDField(models.UUIDField):
        def db_type(self, connection):
            return "uuid"
    
    
    class Model(models.Model):
        id = RealUUIDField(primary_key=True, default=uuid.uuid4, editable=False)
        # Formerly:
        # id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    
  2. Explicitly declare older migrations on the uuid field as a char(36) field. Note that this needs to be char(36) because Django will attempt to store UUIDs with dashes ('06cb5e67-467f-4675-91f3-ca466bcee805' instead of '06cb5e67467f467591f3ca466bcee805'). In migration files, replace models.UUIDField with:

    # app/migrations/0001_migration.py
    class Char36UUIDField(models.UUIDField):
        def db_type(self, connection):
            return "char(36)"
    
    class Migration(migrations.Migration):
        operations = [
            migrations.CreateModel(
                name='Model',
                fields=[
                    ('id', Char36UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
                    # Formerly:
                    # ('id', Char36UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
                ],
            ),
        ]
    
  3. Run ./manage.py makemigrations to generate a migration that will convert the database column from a char(32) to a uuid type.

  4. Install the django==5.0 pip package. Fix any other incompatibilities.

  5. Replace RealUUIDField back to models.UUIDField.