Fixed #23418 -- Fail when migration deconstruct produces invalid import
This commit is contained in:
parent
f9419a6dc0
commit
d28b5f13b3
|
@ -1,3 +1,5 @@
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
def deconstructible(*args, **kwargs):
|
def deconstructible(*args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Class decorator that allow the decorated class to be serialized
|
Class decorator that allow the decorated class to be serialized
|
||||||
|
@ -19,8 +21,25 @@ def deconstructible(*args, **kwargs):
|
||||||
Returns a 3-tuple of class import path, positional arguments,
|
Returns a 3-tuple of class import path, positional arguments,
|
||||||
and keyword arguments.
|
and keyword arguments.
|
||||||
"""
|
"""
|
||||||
|
# Python 2/fallback version
|
||||||
|
if path:
|
||||||
|
module_name, _, name = path.rpartition('.')
|
||||||
|
else:
|
||||||
|
module_name = obj.__module__
|
||||||
|
name = obj.__class__.__name__
|
||||||
|
# Make sure it's actually there and not an inner class
|
||||||
|
module = import_module(module_name)
|
||||||
|
if not hasattr(module, name):
|
||||||
|
raise ValueError(
|
||||||
|
"Could not find object %s in %s.\n"
|
||||||
|
"Please note that you cannot serialize things like inner "
|
||||||
|
"classes. Please move the object into the main module "
|
||||||
|
"body to use migrations.\n"
|
||||||
|
"For more information, see "
|
||||||
|
"https://docs.djangoproject.com/en/dev/topics/migrations/#serializing-values"
|
||||||
|
% (name, module_name))
|
||||||
return (
|
return (
|
||||||
path or '%s.%s' % (obj.__class__.__module__, obj.__class__.__name__),
|
path or '%s.%s' % (obj.__class__.__module__, name),
|
||||||
obj._constructor_args[0],
|
obj._constructor_args[0],
|
||||||
obj._constructor_args[1],
|
obj._constructor_args[1],
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,3 +20,6 @@ Bugfixes
|
||||||
* Fixed serialization of ``type`` objects in migrations (:ticket:`22951`).
|
* Fixed serialization of ``type`` objects in migrations (:ticket:`22951`).
|
||||||
|
|
||||||
* Allowed inline and hidden references to admin fields (:ticket:`23431`).
|
* Allowed inline and hidden references to admin fields (:ticket:`23431`).
|
||||||
|
|
||||||
|
* The ``@deconstructible`` decorator now fails with a ``ValueError`` if the
|
||||||
|
decorated object cannot automatically be imported (:ticket:`23418`).
|
||||||
|
|
|
@ -167,9 +167,17 @@ class WriterTests(TestCase):
|
||||||
self.assertEqual(string, "django.core.validators.EmailValidator(message='hello')")
|
self.assertEqual(string, "django.core.validators.EmailValidator(message='hello')")
|
||||||
self.serialize_round_trip(validator)
|
self.serialize_round_trip(validator)
|
||||||
|
|
||||||
validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello")
|
validator = deconstructible(path="migrations.test_writer.EmailValidator")(EmailValidator)(message="hello")
|
||||||
string = MigrationWriter.serialize(validator)[0]
|
string = MigrationWriter.serialize(validator)[0]
|
||||||
self.assertEqual(string, "custom.EmailValidator(message='hello')")
|
self.assertEqual(string, "migrations.test_writer.EmailValidator(message='hello')")
|
||||||
|
|
||||||
|
validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello")
|
||||||
|
with self.assertRaisesMessage(ImportError, "No module named 'custom'"):
|
||||||
|
MigrationWriter.serialize(validator)
|
||||||
|
|
||||||
|
validator = deconstructible(path="django.core.validators.EmailValidator2")(EmailValidator)(message="hello")
|
||||||
|
with self.assertRaisesMessage(ValueError, "Could not find object EmailValidator2 in django.core.validators."):
|
||||||
|
MigrationWriter.serialize(validator)
|
||||||
|
|
||||||
def test_serialize_empty_nonempty_tuple(self):
|
def test_serialize_empty_nonempty_tuple(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue