Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#30526 closed Bug (invalid)

migration to UUID id field doesn't properly convert integers (SQLite).

Reported by: Martin Baker Owned by: nobody
Component: Migrations Version: dev
Severity: Normal Keywords: UUID
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Martin Baker)

This may be present in other DBs, but I've noticed it with SQLite under Django 2.2.1.

Steps to reproduce:

  1. Start a model with an implicit integer id, and create and run the migration:
from django.db import models

class Something(models.Model):
    name = models.CharField(max_length=32)
  1. Then create one instance via the shell (python manage.py shell):
from uuidbug.models import Something
s = Something(name="One")
s.save()

In the DB it looks like this (can use python manage.py dbshell):

sqlite> .headers on
sqlite> select * from uuiditem_something;
name|id
One|1
  1. Edit the model to use UUIDs:
import uuid

from django.db import models

class Something(models.Model):
    name = models.CharField(max_length=32)
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
  1. After creating and running the migration, add another instance:
from uuidbug.models import Something
s = Something(name="After UUID-ization")
s.save()

DB now looks like this:

sqlite> select * from uuidbug_something;
name|id
One|1
After UUID-ization|693e1942d7d142289bb9fb3664f2c11a

Note that the id for the first instance has not been converted to a UUID properly. If you try to access the objects, you get an error:

>>> Something.objects.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/martin/Connery/opencc-backend/venv/lib/python3.7/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/martin/Connery/opencc-backend/venv/lib/python3.7/site-packages/django/db/models/query.py", line 653, in first
    for obj in (self if self.ordered else self.order_by('pk'))[:1]:
  File "/Users/martin/Connery/opencc-backend/venv/lib/python3.7/site-packages/django/db/models/query.py", line 274, in __iter__
    self._fetch_all()
  File "/Users/martin/Connery/opencc-backend/venv/lib/python3.7/site-packages/django/db/models/query.py", line 1242, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/Users/martin/Connery/opencc-backend/venv/lib/python3.7/site-packages/django/db/models/query.py", line 72, in __iter__
    for row in compiler.results_iter(results):
  File "/Users/martin/Connery/opencc-backend/venv/lib/python3.7/site-packages/django/db/models/sql/compiler.py", line 1044, in apply_converters
    value = converter(value, expression, connection)
  File "/Users/martin/Connery/opencc-backend/venv/lib/python3.7/site-packages/django/db/backends/sqlite3/operations.py", line 294, in convert_uuidfield_value
    value = uuid.UUID(value)
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/uuid.py", line 160, in __init__
    raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string

Change History (3)

comment:1 by Martin Baker, 5 years ago

Description: modified (diff)

comment:2 by Mariusz Felisiak, 5 years ago

Resolution: invalid
Status: newclosed
Summary: migration to UUID id field doesn't properly convert integers (SQLite)migration to UUID id field doesn't properly convert integers (SQLite).
Version: 2.2master

Thanks for this report, however changing column data type is always a sensitive operation and you need to be aware of database caveats. SQLite doesn't have a native UUID data type, so Django handles it as a char(32). Altering data type from integer to char is possible with auto-conversion that's why you didn't get any issue when running this migration. I don't think that there is much that Django can do here.

To fix existing data you can add custom data migration and use Python's uuid.UUID(int=existing_pk) to convert data from int to UUID.

comment:3 by Martin Baker, 5 years ago

I guess to me this just seems like something Django should be able to automagically take care of. This seems like a semi-frequent use case: I made some objects, oh oops I want them to be non-sequential / UUID-y.

Note: See TracTickets for help on using tickets.
Back to Top