Fixes #22325: AttributeError when creating choice set with base choices (#22326)

CHOICE_SETS values (IATA, ISO_3166, UN_LOCODE) are lists of (value, label)
tuples, not dicts.  The .values() call introduced by #21984 treated them as
dicts, raising AttributeError: 'list' object has no attribute 'values' when
full_clean() was invoked during choice set creation.

Replace with a generator expression that extracts the first element from
each tuple, matching the same pattern used elsewhere in the same model.

Also covers the save() path when order_alphabetically=True but
extra_choices is None (base-only choice set), preventing a TypeError
when sorted() receives None.
This commit is contained in:
bctiemann 2026-05-28 15:47:55 -04:00 committed by GitHub
parent 3ee228f69a
commit 352860daf0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 11 additions and 2 deletions

View File

@ -972,7 +972,7 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, Chang
extra_choice_values = set()
if self.base_choices:
valid_choice_values.update(CHOICE_SETS.get(self.base_choices).values())
valid_choice_values.update(value for value, _ in CHOICE_SETS.get(self.base_choices))
if self.extra_choices:
for value, _label in self.extra_choices:
@ -1031,7 +1031,7 @@ class CustomFieldChoiceSet(CloningMixin, ExportTemplatesMixin, OwnerMixin, Chang
def save(self, *args, **kwargs):
# Sort choices if alphabetical ordering is enforced
if self.order_alphabetically:
if self.order_alphabetically and self.extra_choices:
self.extra_choices = sorted(self.extra_choices, key=lambda x: x[0])
return super().save(*args, **kwargs)

View File

@ -466,6 +466,15 @@ class CustomFieldTestCase(TestCase):
self.assertIn('choice_colors', cm.exception.message_dict)
@tag('regression')
def test_choice_set_with_base_choices_validates_without_error(self):
"""Regression test for #22325: base-only choice sets must validate."""
for base in ('IATA', 'ISO_3166', 'UN_LOCODE'):
with self.subTest(base=base):
choice_set = CustomFieldChoiceSet(name=f'Test {base}', base_choices=base, order_alphabetically=True)
choice_set.full_clean() # must not raise
choice_set.save() # must not raise (extra_choices is None)
def test_remove_selected_choice(self):
"""
Removing a ChoiceSet choice that is referenced by an object should raise