Fixed #23950 -- Prevented calling deconstruct on classes in MigrationWriter.

This commit is contained in:
Gavin Wahl 2014-12-02 18:52:58 -07:00 committed by Tim Graham
parent 9d1a69579b
commit dee4d23f7e
3 changed files with 28 additions and 14 deletions

View File

@ -330,6 +330,20 @@ class MigrationWriter(object):
elif isinstance(value, models.Field): elif isinstance(value, models.Field):
attr_name, path, args, kwargs = value.deconstruct() attr_name, path, args, kwargs = value.deconstruct()
return cls.serialize_deconstructed(path, args, kwargs) 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. # Anything that knows how to deconstruct itself.
elif hasattr(value, 'deconstruct'): elif hasattr(value, 'deconstruct'):
return cls.serialize_deconstructed(*value.deconstruct()) return cls.serialize_deconstructed(*value.deconstruct())
@ -364,20 +378,6 @@ class MigrationWriter(object):
"https://docs.djangoproject.com/en/dev/topics/migrations/#serializing-values" "https://docs.djangoproject.com/en/dev/topics/migrations/#serializing-values"
% (value.__name__, module_name)) % (value.__name__, module_name))
return "%s.%s" % (module_name, value.__name__), {"import %s" % module_name} return "%s.%s" % (module_name, value.__name__), {"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__), {"import %s" % module}
# Other iterables # Other iterables
elif isinstance(value, collections.Iterable): elif isinstance(value, collections.Iterable):
imports = set() imports = set()

View File

@ -101,3 +101,6 @@ Bugfixes
* Fixed ``runserver`` crash when socket error message contained Unicode * Fixed ``runserver`` crash when socket error message contained Unicode
characters (:ticket:`23946`). characters (:ticket:`23946`).
* Fixed serialization of ``type`` when adding a ``deconstruct()`` method
(:ticket:`23950`).

View File

@ -340,3 +340,14 @@ class WriterTests(TestCase):
fixed_offset_datetime = datetime.datetime(2014, 1, 1, 1, 1, tzinfo=FixedOffset(180)) fixed_offset_datetime = datetime.datetime(2014, 1, 1, 1, 1, tzinfo=FixedOffset(180))
self.assertEqual(MigrationWriter.serialize_datetime(fixed_offset_datetime), self.assertEqual(MigrationWriter.serialize_datetime(fixed_offset_datetime),
"datetime.datetime(2013, 12, 31, 22, 1, tzinfo=utc)") "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)")