diff --git a/django/forms/fields.py b/django/forms/fields.py index 7f978aa03d62..8aad2d48b832 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -40,6 +40,7 @@ ) from django.utils import formats from django.utils.choices import normalize_choices +from django.utils.datastructures import OrderedSet from django.utils.dateparse import parse_datetime, parse_duration from django.utils.duration import duration_string from django.utils.ipv6 import MAX_IPV6_ADDRESS_LENGTH, clean_ipv6_address @@ -965,8 +966,8 @@ def validate(self, value): if self.required and not value: raise ValidationError(self.error_messages["required"], code="required") # Validate that each value in the value list is in self.choices. - # Use set() to avoid redundant validation. - for val in set(value): + # Avoid redundant validation, and keep elements ordered. + for val in OrderedSet(value): if not self.valid_value(val): raise ValidationError( self.error_messages["invalid_choice"], diff --git a/tests/forms_tests/field_tests/test_multiplechoicefield.py b/tests/forms_tests/field_tests/test_multiplechoicefield.py index 90a47ceee021..0ec53e1b30f4 100644 --- a/tests/forms_tests/field_tests/test_multiplechoicefield.py +++ b/tests/forms_tests/field_tests/test_multiplechoicefield.py @@ -91,5 +91,5 @@ def test_validate_duplicated_values(self): def test_validate_duplicated_invalid_values(self): f = MultipleChoiceField(choices=[("1", "one"), ("2", "Two")]) - with self.assertRaises(ValidationError): - f.validate(["1", "1", "invalid", "invalid"]) + with self.assertRaisesMessage(ValidationError, "invalid-one"): + f.validate(["invalid-one", "invalid-one", "invalid-two"])