Commit e9975ed3 authored by Gavin Wahl's avatar Gavin Wahl Committed by Tim Graham
Browse files

[1.7.x] Fixed #23950 -- Prevented calling deconstruct on classes in MigrationWriter.

Backport of dee4d23f from master
parent de92c61e
Loading
Loading
Loading
Loading
+14 −14
Original line number Diff line number Diff line
@@ -329,6 +329,20 @@ class MigrationWriter(object):
        elif isinstance(value, models.Field):
            attr_name, path, args, kwargs = value.deconstruct()
            return cls.serialize_deconstructed(path, args, kwargs)
        # Classes
        elif isinstance(value, type):
            special_cases = [
                (models.Model, "models.Model", []),
            ]
            for case, string, imports in special_cases:
                if case is value:
                    return string, set(imports)
            if hasattr(value, "__module__"):
                module = value.__module__
                if module == six.moves.builtins.__name__:
                    return value.__name__, set()
                else:
                    return "%s.%s" % (module, value.__name__), {"import %s" % module}
        # Anything that knows how to deconstruct itself.
        elif hasattr(value, 'deconstruct'):
            return cls.serialize_deconstructed(*value.deconstruct())
@@ -362,20 +376,6 @@ class MigrationWriter(object):
                    "For more information, see https://docs.djangoproject.com/en/1.7/topics/migrations/#serializing-values"
                    % (value.__name__, module_name))
            return "%s.%s" % (module_name, value.__name__), set(["import %s" % module_name])
        # Classes
        elif isinstance(value, type):
            special_cases = [
                (models.Model, "models.Model", []),
            ]
            for case, string, imports in special_cases:
                if case is value:
                    return string, set(imports)
            if hasattr(value, "__module__"):
                module = value.__module__
                if module == six.moves.builtins.__name__:
                    return value.__name__, set()
                else:
                    return "%s.%s" % (module, value.__name__), set(["import %s" % module])
        # Other iterables
        elif isinstance(value, collections.Iterable):
            imports = set()
+3 −0
Original line number Diff line number Diff line
@@ -101,3 +101,6 @@ Bugfixes

* Fixed ``runserver`` crash when socket error message contained Unicode
  characters (:ticket:`23946`).

* Fixed serialization of ``type`` when adding a ``deconstruct()`` method
  (:ticket:`23950`).
+11 −0
Original line number Diff line number Diff line
@@ -340,3 +340,14 @@ class WriterTests(TestCase):
        fixed_offset_datetime = datetime.datetime(2014, 1, 1, 1, 1, tzinfo=FixedOffset(180))
        self.assertEqual(MigrationWriter.serialize_datetime(fixed_offset_datetime),
                         "datetime.datetime(2013, 12, 31, 22, 1, tzinfo=utc)")

    def test_deconstruct_class_arguments(self):
        # Yes, it doesn't make sense to use a class as a default for a
        # CharField. It does make sense for custom fields though, for example
        # an enumfield that takes the enum class as an argument.
        class DeconstructableInstances(object):
            def deconstruct(self):
                return ('DeconstructableInstances', [], {})

        string = MigrationWriter.serialize(models.CharField(default=DeconstructableInstances))[0]
        self.assertEqual(string, "models.CharField(default=migrations.test_writer.DeconstructableInstances)")