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:
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()
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue