Fixed #20039 -- Fixed has_changed form detection for required TypedChoiceFields
Thanks Florian Apolloner for the report and the review. Also fixes #19643.
This commit is contained in:
parent
6b4834952d
commit
9883551d50
|
@ -778,14 +778,15 @@ class ChoiceField(Field):
|
||||||
|
|
||||||
def valid_value(self, value):
|
def valid_value(self, value):
|
||||||
"Check to see if the provided value is a valid choice"
|
"Check to see if the provided value is a valid choice"
|
||||||
|
text_value = force_text(value)
|
||||||
for k, v in self.choices:
|
for k, v in self.choices:
|
||||||
if isinstance(v, (list, tuple)):
|
if isinstance(v, (list, tuple)):
|
||||||
# This is an optgroup, so look inside the group for options
|
# This is an optgroup, so look inside the group for options
|
||||||
for k2, v2 in v:
|
for k2, v2 in v:
|
||||||
if value == smart_text(k2):
|
if value == k2 or text_value == force_text(k2):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if value == smart_text(k):
|
if value == k or text_value == force_text(k):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -801,7 +802,6 @@ class TypedChoiceField(ChoiceField):
|
||||||
right type.
|
right type.
|
||||||
"""
|
"""
|
||||||
value = super(TypedChoiceField, self).to_python(value)
|
value = super(TypedChoiceField, self).to_python(value)
|
||||||
super(TypedChoiceField, self).validate(value)
|
|
||||||
if value == self.empty_value or value in self.empty_values:
|
if value == self.empty_value or value in self.empty_values:
|
||||||
return self.empty_value
|
return self.empty_value
|
||||||
try:
|
try:
|
||||||
|
@ -810,9 +810,6 @@ class TypedChoiceField(ChoiceField):
|
||||||
raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
|
raise ValidationError(self.error_messages['invalid_choice'] % {'value': value})
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MultipleChoiceField(ChoiceField):
|
class MultipleChoiceField(ChoiceField):
|
||||||
hidden_widget = MultipleHiddenInput
|
hidden_widget = MultipleHiddenInput
|
||||||
|
@ -864,7 +861,6 @@ class TypedMultipleChoiceField(MultipleChoiceField):
|
||||||
right type.
|
right type.
|
||||||
"""
|
"""
|
||||||
value = super(TypedMultipleChoiceField, self).to_python(value)
|
value = super(TypedMultipleChoiceField, self).to_python(value)
|
||||||
super(TypedMultipleChoiceField, self).validate(value)
|
|
||||||
if value == self.empty_value or value in self.empty_values:
|
if value == self.empty_value or value in self.empty_values:
|
||||||
return self.empty_value
|
return self.empty_value
|
||||||
new_value = []
|
new_value = []
|
||||||
|
@ -876,7 +872,11 @@ class TypedMultipleChoiceField(MultipleChoiceField):
|
||||||
return new_value
|
return new_value
|
||||||
|
|
||||||
def validate(self, value):
|
def validate(self, value):
|
||||||
pass
|
if value != self.empty_value:
|
||||||
|
super(TypedMultipleChoiceField, self).validate(value)
|
||||||
|
elif self.required:
|
||||||
|
raise ValidationError(self.error_messages['required'])
|
||||||
|
|
||||||
|
|
||||||
class ComboField(Field):
|
class ComboField(Field):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -911,6 +911,11 @@ class FieldsTests(SimpleTestCase):
|
||||||
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None)
|
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None)
|
||||||
self.assertEqual(None, f.clean(''))
|
self.assertEqual(None, f.clean(''))
|
||||||
|
|
||||||
|
def test_typedchoicefield_has_changed(self):
|
||||||
|
# has_changed should not trigger required validation
|
||||||
|
f = TypedChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
|
||||||
|
self.assertFalse(f._has_changed(None, ''))
|
||||||
|
|
||||||
# NullBooleanField ############################################################
|
# NullBooleanField ############################################################
|
||||||
|
|
||||||
def test_nullbooleanfield_1(self):
|
def test_nullbooleanfield_1(self):
|
||||||
|
@ -1060,6 +1065,11 @@ class FieldsTests(SimpleTestCase):
|
||||||
f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None)
|
f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=False, empty_value=None)
|
||||||
self.assertEqual(None, f.clean([]))
|
self.assertEqual(None, f.clean([]))
|
||||||
|
|
||||||
|
def test_typedmultiplechoicefield_has_changed(self):
|
||||||
|
# has_changed should not trigger required validation
|
||||||
|
f = TypedMultipleChoiceField(choices=[(1, "+1"), (-1, "-1")], coerce=int, required=True)
|
||||||
|
self.assertFalse(f._has_changed(None, ''))
|
||||||
|
|
||||||
# ComboField ##################################################################
|
# ComboField ##################################################################
|
||||||
|
|
||||||
def test_combofield_1(self):
|
def test_combofield_1(self):
|
||||||
|
|
|
@ -61,10 +61,10 @@ class FormsRegressionsTestCase(TestCase):
|
||||||
UNITS = ((b'\xd0\xbc\xd0\xb5\xd1\x81.', b'\xd0\xbc\xd0\xb5\xd1\x81.'),
|
UNITS = ((b'\xd0\xbc\xd0\xb5\xd1\x81.', b'\xd0\xbc\xd0\xb5\xd1\x81.'),
|
||||||
(b'\xd1\x88\xd1\x82.', b'\xd1\x88\xd1\x82.'))
|
(b'\xd1\x88\xd1\x82.', b'\xd1\x88\xd1\x82.'))
|
||||||
f = ChoiceField(choices=UNITS)
|
f = ChoiceField(choices=UNITS)
|
||||||
self.assertEqual(f.clean('\u0448\u0442.'), '\u0448\u0442.')
|
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
# Ignore UnicodeWarning
|
# Ignore UnicodeWarning
|
||||||
warnings.simplefilter("ignore")
|
warnings.simplefilter("ignore")
|
||||||
|
self.assertEqual(f.clean('\u0448\u0442.'), '\u0448\u0442.')
|
||||||
self.assertEqual(f.clean(b'\xd1\x88\xd1\x82.'), '\u0448\u0442.')
|
self.assertEqual(f.clean(b'\xd1\x88\xd1\x82.'), '\u0448\u0442.')
|
||||||
|
|
||||||
# Translated error messages used to be buggy.
|
# Translated error messages used to be buggy.
|
||||||
|
|
Loading…
Reference in New Issue