#27378: makemigration unable to serialize UUID when UUIDField have choices set
-----------------------------+------------------------------------
     Reporter:  make.kernel  |      Owner:  nobody
         Type:  Bug          |     Status:  new
    Component:  Migrations   |    Version:  1.10
     Severity:  Normal       |   Keywords:  uuid choices migration
 Triage Stage:  Unreviewed   |  Has patch:  1
Easy pickings:  0            |      UI/UX:  0
-----------------------------+------------------------------------
 Using following model

 {{{
 import uuid
 from django.db import models

 # Create your models here.

 class Student(models.Model):
     FRESHMAN = uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8')
     SOPHOMORE = uuid.UUID('c7853ec1-2ea3-4359-b02d-b54e8f1bcee2')
     JUNIOR = uuid.UUID('94a43637-6dfc-4209-852a-bd9bd8c0101f')
     SENIOR = uuid.UUID('dfb9cc50-c332-4809-9b4c-69c81a7489d0')
     MENTOR = uuid.UUID('b8f3897a-0a0a-4630-8f1b-1ca4b0ee37ca')
     YEAR_IN_SCHOOL_CHOICES = (
         (FRESHMAN, 'Freshman'),
         (SOPHOMORE, 'Sophomore'),
         (JUNIOR, 'Junior'),
         (SENIOR, 'Senior'),
         (MENTOR, 'Mentor'),
     )
     year_in_school = models.UUIDField(choices=YEAR_IN_SCHOOL_CHOICES,
 default=FRESHMAN)
 }}}

 According to documentation it should work, and it works until you try to
 make migrations for this model, it fails with exception

 {{{
 Migrations for 'testapp':
   testapp/migrations/0001_initial.py:
     - Create model Student
 Traceback (most recent call last):
   File "./manage.py", line 22, in <module>
     execute_from_command_line(sys.argv)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/core/management/__init__.py", line 367, in
 execute_from_command_line
     utility.execute()
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/core/management/__init__.py", line 359, in execute
     self.fetch_command(subcommand).run_from_argv(self.argv)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/core/management/base.py", line 294, in run_from_argv
     self.execute(*args, **cmd_options)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/core/management/base.py", line 345, in execute
     output = self.handle(*args, **options)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/core/management/commands/makemigrations.py", line 189, in
 handle
     self.write_migration_files(changes)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/core/management/commands/makemigrations.py", line 224, in
 write_migration_files
     migration_string = writer.as_string()
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/writer.py", line 163, in as_string
     operation_string, operation_imports =
 OperationWriter(operation).serialize()
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/writer.py", line 120, in serialize
     _write(arg_name, arg_value)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/writer.py", line 84, in _write
     arg_string, arg_imports = MigrationWriter.serialize(_arg_value)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/writer.py", line 293, in serialize
     return serializer_factory(value).serialize()
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/serializer.py", line 228, in serialize
     return self.serialize_deconstructed(path, args, kwargs)
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/serializer.py", line 100, in
 serialize_deconstructed
     arg_string, arg_imports = serializer_factory(arg).serialize()
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/serializer.py", line 43, in serialize
     item_string, item_imports = serializer_factory(item).serialize()
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/serializer.py", line 43, in serialize
     item_string, item_imports = serializer_factory(item).serialize()
   File "/home/yuriy/dev/uuidtest/env/lib/python3.5/site-
 packages/django/db/migrations/serializer.py", line 388, in
 serializer_factory
     "topics/migrations/#migration-serializing" % (value,
 get_docs_version())
 ValueError: Cannot serialize: UUID('5c859437-d061-4847-b3f7-e6b78852f8c8')
 There are some values Django cannot serialize into migration files.
 For more, see https://docs.djangoproject.com/en/1.10/topics/migrations
 /#migration-serializing
 }}}

 Here is a small patch that add UUIDSerializer to migration serializers and
 tells serializer_factory to use it in case of UUID value

 {{{
 --- django/db/migrations/serializer.py.orig     2016-10-24
 12:16:21.000000000 +0300
 +++ django/db/migrations/serializer.py  2016-10-24 15:32:33.908818046
 +0300
 @@ -6,6 +6,7 @@
  import functools
  import math
  import types
 +import uuid
  from importlib import import_module

  from django.db import models
 @@ -319,6 +320,10 @@
              else:
                  return "%s.%s" % (module, self.value.__name__), {"import
 %s" % module}

 +class UUIDSerializer(BaseSerializer):
 +    def serialize(self):
 +        return "uuid.UUID('%s')" % self.value, {"import uuid"}
 +

  def serializer_factory(value):
      from django.db.migrations.writer import SettingsReference
 @@ -382,6 +387,8 @@
          return IterableSerializer(value)
      if isinstance(value, (COMPILED_REGEX_TYPE, RegexObject)):
          return RegexSerializer(value)
 +    if isinstance(value, uuid.UUID):
 +        return  UUIDSerializer(value)
      raise ValueError(
          "Cannot serialize: %r\nThere are some values Django cannot
 serialize into "
          "migration files.\nFor more, see
 https://docs.djangoproject.com/en/%s/";
 }}}


 After applying this change everything works as expected


 {{{
 (env) yuriy@yuriy-desktop:~/dev/uuidtest/src/uuidtest$ ./manage.py
 makemigrations
 Migrations for 'testapp':
   testapp/migrations/0001_initial.py:
     - Create model Student
 (env) yuriy@yuriy-desktop:~/dev/uuidtest/src/uuidtest$ ./manage.py migrate
 Operations to perform:
   Apply all migrations: admin, auth, contenttypes, sessions, testapp
 Running migrations:
   Applying contenttypes.0001_initial... OK
   Applying auth.0001_initial... OK
   Applying admin.0001_initial... OK
   Applying admin.0002_logentry_remove_auto_add... OK
   Applying contenttypes.0002_remove_content_type_name... OK
   Applying auth.0002_alter_permission_name_max_length... OK
   Applying auth.0003_alter_user_email_max_length... OK
   Applying auth.0004_alter_user_username_opts... OK
   Applying auth.0005_alter_user_last_login_null... OK
   Applying auth.0006_require_contenttypes_0002... OK
   Applying auth.0007_alter_validators_add_error_messages... OK
   Applying auth.0008_alter_user_username_max_length... OK
   Applying sessions.0001_initial... OK
   Applying testapp.0001_initial... OK
 (env) yuriy@yuriy-desktop:~/dev/uuidtest/src/uuidtest$
 }}}


 migratin produced is


 {{{
 # -*- coding: utf-8 -*-
 # Generated by Django 1.10.2 on 2016-10-24 13:30
 from __future__ import unicode_literals

 from django.db import migrations, models
 import uuid


 class Migration(migrations.Migration):

     initial = True

     dependencies = [
     ]

     operations = [
         migrations.CreateModel(
             name='Student',
             fields=[
                 ('id', models.AutoField(auto_created=True,
 primary_key=True, serialize=False, verbose_name='ID')),
                 ('year_in_school',
 models.UUIDField(choices=[(uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8'),
 'Freshman'), (uuid.UUID('c7853ec1-2ea3-4359-b02d-b54e8f1bcee2'),
 'Sophomore'), (uuid.UUID('94a43637-6dfc-4209-852a-bd9bd8c0101f'),
 'Junior'), (uuid.UUID('dfb9cc50-c332-4809-9b4c-69c81a7489d0'), 'Senior'),
 (uuid.UUID('b8f3897a-0a0a-4630-8f1b-1ca4b0ee37ca'), 'Mentor')],
 default=uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8'))),
             ],
         ),
     ]
 }}}

 I have no idea if it is correct, but works for me, if it should be fixed
 in other place could you point me where to look?

 Thanks

--
Ticket URL: <https://code.djangoproject.com/ticket/27378>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/054.e1b0e6965f5b9bbabc07d774963b49d0%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to