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):
|
||||
"""
|
||||
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,
|
||||
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 (
|
||||
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[1],
|
||||
)
|
||||
|
|
|
@ -20,3 +20,6 @@ Bugfixes
|
|||
* Fixed serialization of ``type`` objects in migrations (:ticket:`22951`).
|
||||
|
||||
* 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.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]
|
||||
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):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue