app/notes/20231228-0130.md
Updating UUIDField on MariaDB to Django 5
django-5-mariadb-uuidfield
1703727004
Updating from Django 4 to Django 5 comes with an incompatible change to how
Django `UUIDField`s are stored in MariaDB databases - in Django 4, Django
would store `UUIDField`s as `char(32)` types but in Django 5, Django would
store `UUIDField`s as `uuid` types. However, the [Django 5 upgrade notes are
incomplete](https://docs.djangoproject.com/en/5.0/releases/5.0/#migrating-uuidfield). 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:
```python
# 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:
```python
# 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`.