mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #28393 -- Added helpful error messages for invalid AutoField/FloatField/IntegerField values.
Co-authored-by: Diederik van der Boor <vdboor@edoburu.nl> Co-authored-by: Nick Pope <nick.pope@flightdataservices.com>
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							1af469e67f
						
					
				
				
					commit
					25f21bd237
				
			| @@ -964,7 +964,12 @@ class AutoField(Field): | ||||
|         value = super().get_prep_value(value) | ||||
|         if value is None or isinstance(value, OuterRef): | ||||
|             return value | ||||
|         return int(value) | ||||
|         try: | ||||
|             return int(value) | ||||
|         except (TypeError, ValueError) as e: | ||||
|             raise e.__class__( | ||||
|                 "Field '%s' expected a number but got %r." % (self.name, value), | ||||
|             ) from e | ||||
|  | ||||
|     def contribute_to_class(self, cls, name, **kwargs): | ||||
|         assert not cls._meta.auto_field, "Model %s can't have more than one AutoField." % cls._meta.label | ||||
| @@ -1745,7 +1750,12 @@ class FloatField(Field): | ||||
|         value = super().get_prep_value(value) | ||||
|         if value is None: | ||||
|             return None | ||||
|         return float(value) | ||||
|         try: | ||||
|             return float(value) | ||||
|         except (TypeError, ValueError) as e: | ||||
|             raise e.__class__( | ||||
|                 "Field '%s' expected a number but got %r." % (self.name, value), | ||||
|             ) from e | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "FloatField" | ||||
| @@ -1827,7 +1837,12 @@ class IntegerField(Field): | ||||
|         value = super().get_prep_value(value) | ||||
|         if value is None: | ||||
|             return None | ||||
|         return int(value) | ||||
|         try: | ||||
|             return int(value) | ||||
|         except (TypeError, ValueError) as e: | ||||
|             raise e.__class__( | ||||
|                 "Field '%s' expected a number but got %r." % (self.name, value), | ||||
|             ) from e | ||||
|  | ||||
|     def get_internal_type(self): | ||||
|         return "IntegerField" | ||||
|   | ||||
| @@ -92,6 +92,18 @@ class UnicodeSlugField(models.Model): | ||||
|     s = models.SlugField(max_length=255, allow_unicode=True) | ||||
|  | ||||
|  | ||||
| class AutoModel(models.Model): | ||||
|     value = models.AutoField(primary_key=True) | ||||
|  | ||||
|  | ||||
| class BigAutoModel(models.Model): | ||||
|     value = models.BigAutoField(primary_key=True) | ||||
|  | ||||
|  | ||||
| class SmallAutoModel(models.Model): | ||||
|     value = models.SmallAutoField(primary_key=True) | ||||
|  | ||||
|  | ||||
| class SmallIntegerModel(models.Model): | ||||
|     value = models.SmallIntegerField() | ||||
|  | ||||
|   | ||||
							
								
								
									
										32
									
								
								tests/model_fields/test_autofield.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								tests/model_fields/test_autofield.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| from django.test import TestCase | ||||
|  | ||||
| from .models import AutoModel, BigAutoModel, SmallAutoModel | ||||
|  | ||||
|  | ||||
| class AutoFieldTests(TestCase): | ||||
|     model = AutoModel | ||||
|  | ||||
|     def test_invalid_value(self): | ||||
|         tests = [ | ||||
|             (TypeError, ()), | ||||
|             (TypeError, []), | ||||
|             (TypeError, {}), | ||||
|             (TypeError, set()), | ||||
|             (TypeError, object()), | ||||
|             (TypeError, complex()), | ||||
|             (ValueError, 'non-numeric string'), | ||||
|             (ValueError, b'non-numeric byte-string'), | ||||
|         ] | ||||
|         for exception, value in tests: | ||||
|             with self.subTest(value=value): | ||||
|                 msg = "Field 'value' expected a number but got %r." % (value,) | ||||
|                 with self.assertRaisesMessage(exception, msg): | ||||
|                     self.model.objects.create(value=value) | ||||
|  | ||||
|  | ||||
| class BigAutoFieldTests(AutoFieldTests): | ||||
|     model = BigAutoModel | ||||
|  | ||||
|  | ||||
| class SmallAutoFieldTests(AutoFieldTests): | ||||
|     model = SmallAutoModel | ||||
| @@ -31,3 +31,20 @@ class TestFloatField(TestCase): | ||||
|         obj.size = obj | ||||
|         with self.assertRaisesMessage(TypeError, msg): | ||||
|             obj.save() | ||||
|  | ||||
|     def test_invalid_value(self): | ||||
|         tests = [ | ||||
|             (TypeError, ()), | ||||
|             (TypeError, []), | ||||
|             (TypeError, {}), | ||||
|             (TypeError, set()), | ||||
|             (TypeError, object()), | ||||
|             (TypeError, complex()), | ||||
|             (ValueError, 'non-numeric string'), | ||||
|             (ValueError, b'non-numeric byte-string'), | ||||
|         ] | ||||
|         for exception, value in tests: | ||||
|             with self.subTest(value): | ||||
|                 msg = "Field 'size' expected a number but got %r." % (value,) | ||||
|                 with self.assertRaisesMessage(exception, msg): | ||||
|                     FloatModel.objects.create(size=value) | ||||
|   | ||||
| @@ -137,6 +137,23 @@ class IntegerFieldTests(TestCase): | ||||
|         instance = self.model.objects.get(value='10') | ||||
|         self.assertEqual(instance.value, 10) | ||||
|  | ||||
|     def test_invalid_value(self): | ||||
|         tests = [ | ||||
|             (TypeError, ()), | ||||
|             (TypeError, []), | ||||
|             (TypeError, {}), | ||||
|             (TypeError, set()), | ||||
|             (TypeError, object()), | ||||
|             (TypeError, complex()), | ||||
|             (ValueError, 'non-numeric string'), | ||||
|             (ValueError, b'non-numeric byte-string'), | ||||
|         ] | ||||
|         for exception, value in tests: | ||||
|             with self.subTest(value): | ||||
|                 msg = "Field 'value' expected a number but got %r." % (value,) | ||||
|                 with self.assertRaisesMessage(exception, msg): | ||||
|                     self.model.objects.create(value=value) | ||||
|  | ||||
|  | ||||
| class SmallIntegerFieldTests(IntegerFieldTests): | ||||
|     model = SmallIntegerModel | ||||
|   | ||||
| @@ -3853,7 +3853,7 @@ class TestTicket24279(TestCase): | ||||
|  | ||||
| class TestInvalidValuesRelation(SimpleTestCase): | ||||
|     def test_invalid_values(self): | ||||
|         msg = "invalid literal for int() with base 10: 'abc'" | ||||
|         msg = "Field 'id' expected a number but got 'abc'." | ||||
|         with self.assertRaisesMessage(ValueError, msg): | ||||
|             Annotation.objects.filter(tag='abc') | ||||
|         with self.assertRaisesMessage(ValueError, msg): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user