mirror of
				https://github.com/django/django.git
				synced 2025-10-29 00:26:07 +00:00 
			
		
		
		
	[1.7.x] Fixed #23418 -- Fail when migration deconstruct produces invalid import
Backport of d28b5f13b3 from master
			
			
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							9c4fb019cb
						
					
				
				
					commit
					b0def3bcac
				
			| @@ -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): | ||||
|         """ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user