diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 2feded85c7..37c11f1241 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -547,25 +547,23 @@ class ChoiceWidget(Widget): def optgroups(self, name, value, attrs=None): """Return a list of optgroups for this widget.""" - default = (None, [], 0) - groups = [default] + groups = [] has_selected = False - for option_value, option_label in chain(self.choices): + for index, (option_value, option_label) in enumerate(chain(self.choices)): if option_value is None: option_value = '' + subgroup = [] if isinstance(option_label, (list, tuple)): - index = groups[-1][2] + 1 + group_name = option_value subindex = 0 - subgroup = [] - groups.append((option_value, subgroup, index)) choices = option_label else: - index = len(default[1]) - subgroup = default[1] + group_name = None subindex = None choices = [(option_value, option_label)] + groups.append((group_name, subgroup, index)) for subvalue, sublabel in choices: selected = ( diff --git a/docs/releases/1.11.1.txt b/docs/releases/1.11.1.txt index 64394aa755..9a9d96bf46 100644 --- a/docs/releases/1.11.1.txt +++ b/docs/releases/1.11.1.txt @@ -78,3 +78,6 @@ Bugfixes * Prevented migrations from dropping database indexes from ``Meta.indexes`` when changing ``Field.db_index`` to ``False`` (:ticket:`28052`). + +* Fixed a regression in choice ordering in form fields with grouped and + non-grouped options (:ticket:`28157`). diff --git a/tests/forms_tests/widget_tests/test_select.py b/tests/forms_tests/widget_tests/test_select.py index 047d007564..a5201e96e8 100644 --- a/tests/forms_tests/widget_tests/test_select.py +++ b/tests/forms_tests/widget_tests/test_select.py @@ -280,35 +280,73 @@ class SelectTest(WidgetTest): groups = list(self.widget(choices=choices).optgroups( 'name', ['vhs'], attrs={'class': 'super'}, )) - self.assertEqual(len(groups), 3) - self.assertEqual(groups[0][0], None) - self.assertEqual(groups[0][2], 0) - self.assertEqual(len(groups[0][1]), 1) - options = groups[0][1] - self.assertEqual(options[0]['name'], 'name') - self.assertEqual(options[0]['value'], 'unknown') - self.assertEqual(options[0]['label'], 'Unknown') - self.assertEqual(options[0]['index'], '0') - self.assertEqual(options[0]['selected'], False) - self.assertEqual(groups[1][0], 'Audio') - self.assertEqual(groups[1][2], 1) - self.assertEqual(len(groups[1][1]), 2) - options = groups[1][1] - self.assertEqual(options[0]['name'], 'name') - self.assertEqual(options[0]['value'], 'vinyl') - self.assertEqual(options[0]['label'], 'Vinyl') - self.assertEqual(options[0]['index'], '1_0') - self.assertEqual(options[1]['index'], '1_1') - self.assertEqual(groups[2][0], 'Video') - self.assertEqual(groups[2][2], 2) - self.assertEqual(len(groups[2][1]), 2) - options = groups[2][1] - self.assertEqual(options[0]['name'], 'name') - self.assertEqual(options[0]['value'], 'vhs') - self.assertEqual(options[0]['label'], 'VHS Tape') - self.assertEqual(options[0]['index'], '2_0') - self.assertEqual(options[0]['selected'], True) - self.assertEqual(options[1]['index'], '2_1') + audio, video, unknown = groups + label, options, index = audio + self.assertEqual(label, 'Audio') + self.assertEqual( + options, + [{ + 'value': 'vinyl', + 'type': 'select', + 'attrs': {}, + 'index': '0_0', + 'label': 'Vinyl', + 'template_name': 'django/forms/widgets/select_option.html', + 'name': 'name', + 'selected': False, + }, { + 'value': 'cd', + 'type': 'select', + 'attrs': {}, + 'index': '0_1', + 'label': 'CD', + 'template_name': 'django/forms/widgets/select_option.html', + 'name': 'name', + 'selected': False, + }] + ) + self.assertEqual(index, 0) + label, options, index = video + self.assertEqual(label, 'Video') + self.assertEqual( + options, + [{ + 'value': 'vhs', + 'template_name': 'django/forms/widgets/select_option.html', + 'label': 'VHS Tape', + 'attrs': {'selected': True}, + 'index': '1_0', + 'name': 'name', + 'selected': True, + 'type': 'select', + }, { + 'value': 'dvd', + 'template_name': 'django/forms/widgets/select_option.html', + 'label': 'DVD', + 'attrs': {}, + 'index': '1_1', + 'name': 'name', + 'selected': False, + 'type': 'select', + }] + ) + self.assertEqual(index, 1) + label, options, index = unknown + self.assertEqual(label, None) + self.assertEqual( + options, + [{ + 'value': 'unknown', + 'selected': False, + 'template_name': 'django/forms/widgets/select_option.html', + 'label': 'Unknown', + 'attrs': {}, + 'index': '2', + 'name': 'name', + 'type': 'select', + }] + ) + self.assertEqual(index, 2) def test_deepcopy(self): """