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)
|
value = super().get_prep_value(value)
|
||||||
if value is None or isinstance(value, OuterRef):
|
if value is None or isinstance(value, OuterRef):
|
||||||
return value
|
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):
|
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
|
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)
|
value = super().get_prep_value(value)
|
||||||
if value is None:
|
if value is None:
|
||||||
return 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):
|
def get_internal_type(self):
|
||||||
return "FloatField"
|
return "FloatField"
|
||||||
|
@ -1827,7 +1837,12 @@ class IntegerField(Field):
|
||||||
value = super().get_prep_value(value)
|
value = super().get_prep_value(value)
|
||||||
if value is None:
|
if value is None:
|
||||||
return 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):
|
def get_internal_type(self):
|
||||||
return "IntegerField"
|
return "IntegerField"
|
||||||
|
|
|
@ -92,6 +92,18 @@ class UnicodeSlugField(models.Model):
|
||||||
s = models.SlugField(max_length=255, allow_unicode=True)
|
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):
|
class SmallIntegerModel(models.Model):
|
||||||
value = models.SmallIntegerField()
|
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
|
obj.size = obj
|
||||||
with self.assertRaisesMessage(TypeError, msg):
|
with self.assertRaisesMessage(TypeError, msg):
|
||||||
obj.save()
|
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')
|
instance = self.model.objects.get(value='10')
|
||||||
self.assertEqual(instance.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):
|
class SmallIntegerFieldTests(IntegerFieldTests):
|
||||||
model = SmallIntegerModel
|
model = SmallIntegerModel
|
||||||
|
|
|
@ -3853,7 +3853,7 @@ class TestTicket24279(TestCase):
|
||||||
|
|
||||||
class TestInvalidValuesRelation(SimpleTestCase):
|
class TestInvalidValuesRelation(SimpleTestCase):
|
||||||
def test_invalid_values(self):
|
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):
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
Annotation.objects.filter(tag='abc')
|
Annotation.objects.filter(tag='abc')
|
||||||
with self.assertRaisesMessage(ValueError, msg):
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
|
|
Loading…
Reference in New Issue