Loading django/utils/deconstruct.py +20 −1 Original line number Diff line number Diff line from importlib import import_module def deconstructible(*args, **kwargs): """ Class decorator that allow the decorated class to be serialized Loading @@ -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], ) Loading docs/releases/1.7.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -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`). tests/migrations/test_writer.py +10 −2 Original line number Diff line number Diff line Loading @@ -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 Loading
django/utils/deconstruct.py +20 −1 Original line number Diff line number Diff line from importlib import import_module def deconstructible(*args, **kwargs): """ Class decorator that allow the decorated class to be serialized Loading @@ -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], ) Loading
docs/releases/1.7.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -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`).
tests/migrations/test_writer.py +10 −2 Original line number Diff line number Diff line Loading @@ -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