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:
Diederik van der Boor 2017-07-13 13:55:23 +02:00 committed by Mariusz Felisiak
parent 1af469e67f
commit 25f21bd237
6 changed files with 97 additions and 4 deletions

View File

@ -964,7 +964,12 @@ class AutoField(Field):
value = super().get_prep_value(value)
if value is None or isinstance(value, OuterRef):
return 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
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
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"

View File

@ -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()

View 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

View File

@ -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)

View File

@ -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

View File

@ -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):