diff --git a/django/forms/widgets.py b/django/forms/widgets.py index e4f8957189..eed1fa5c3b 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -696,27 +696,35 @@ class NullBooleanSelect(Select): """ def __init__(self, attrs=None): choices = ( - ('1', _('Unknown')), - ('2', _('Yes')), - ('3', _('No')), + ('unknown', _('Unknown')), + ('true', _('Yes')), + ('false', _('No')), ) super().__init__(attrs, choices) def format_value(self, value): try: - return {True: '2', False: '3', '2': '2', '3': '3'}[value] + return { + True: 'true', False: 'false', + 'true': 'true', 'false': 'false', + # For backwards compatibility with Django < 2.2. + '2': 'true', '3': 'false', + }[value] except KeyError: - return '1' + return 'unknown' def value_from_datadict(self, data, files, name): value = data.get(name) return { - '2': True, True: True, 'True': True, - '3': False, 'False': False, False: False, + 'true': True, + 'false': False, + # For backwards compatibility with Django < 2.2. + '2': True, + '3': False, }.get(value) diff --git a/docs/releases/2.2.txt b/docs/releases/2.2.txt index debc60c5f6..a8bd9ab488 100644 --- a/docs/releases/2.2.txt +++ b/docs/releases/2.2.txt @@ -380,6 +380,11 @@ Miscellaneous * Support for ``cx_Oracle`` < 6.0 is removed. +* In an attempt to provide more semantic query data, ``NullBooleanSelect`` now + renders ``<option>`` values of ``unknown``, ``true``, and ``false`` instead + of ``1``, ``2``, and ``3``. For backwards compatibility, the old values are + still accepted as data. + .. _deprecated-features-2.2: Features deprecated in 2.2 diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index ba233e72ff..bad24b5230 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -2356,39 +2356,57 @@ Password: <input type="password" name="password" required> p = Person({'name': 'Joe'}, auto_id=False) self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> -<option value="1" selected>Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> +<option value="unknown" selected>Unknown</option> +<option value="true">Yes</option> +<option value="false">No</option> </select>""") p = Person({'name': 'Joe', 'is_cool': '1'}, auto_id=False) self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> -<option value="1" selected>Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> +<option value="unknown" selected>Unknown</option> +<option value="true">Yes</option> +<option value="false">No</option> </select>""") p = Person({'name': 'Joe', 'is_cool': '2'}, auto_id=False) self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected>Yes</option> -<option value="3">No</option> +<option value="unknown">Unknown</option> +<option value="true" selected>Yes</option> +<option value="false">No</option> </select>""") p = Person({'name': 'Joe', 'is_cool': '3'}, auto_id=False) self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected>No</option> +<option value="unknown">Unknown</option> +<option value="true">Yes</option> +<option value="false" selected>No</option> </select>""") p = Person({'name': 'Joe', 'is_cool': True}, auto_id=False) self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2" selected>Yes</option> -<option value="3">No</option> +<option value="unknown">Unknown</option> +<option value="true" selected>Yes</option> +<option value="false">No</option> </select>""") p = Person({'name': 'Joe', 'is_cool': False}, auto_id=False) self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> -<option value="1">Unknown</option> -<option value="2">Yes</option> -<option value="3" selected>No</option> +<option value="unknown">Unknown</option> +<option value="true">Yes</option> +<option value="false" selected>No</option> +</select>""") + p = Person({'name': 'Joe', 'is_cool': 'unknown'}, auto_id=False) + self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> +<option value="unknown" selected>Unknown</option> +<option value="true">Yes</option> +<option value="false">No</option> +</select>""") + p = Person({'name': 'Joe', 'is_cool': 'true'}, auto_id=False) + self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> +<option value="unknown">Unknown</option> +<option value="true" selected>Yes</option> +<option value="false">No</option> +</select>""") + p = Person({'name': 'Joe', 'is_cool': 'false'}, auto_id=False) + self.assertHTMLEqual(str(p['is_cool']), """<select name="is_cool"> +<option value="unknown">Unknown</option> +<option value="true">Yes</option> +<option value="false" selected>No</option> </select>""") def test_forms_with_file_fields(self): @@ -2758,9 +2776,9 @@ Good luck picking a username that doesn't already exist.</p> <label class="required" for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></li> <li class="required"><label class="required" for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool"> -<option value="1" selected>Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> +<option value="unknown" selected>Unknown</option> +<option value="true">Yes</option> +<option value="false">No</option> </select></li> <li><label for="id_email">Email:</label> <input type="email" name="email" id="id_email"></li> <li class="required error"><ul class="errorlist"><li>This field is required.</li></ul> @@ -2774,9 +2792,9 @@ Good luck picking a username that doesn't already exist.</p> <input type="text" name="name" id="id_name" required></p> <p class="required"><label class="required" for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool"> -<option value="1" selected>Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> +<option value="unknown" selected>Unknown</option> +<option value="true">Yes</option> +<option value="false">No</option> </select></p> <p><label for="id_email">Email:</label> <input type="email" name="email" id="id_email"></p> <ul class="errorlist"><li>This field is required.</li></ul> @@ -2792,9 +2810,9 @@ Good luck picking a username that doesn't already exist.</p> <input type="text" name="name" id="id_name" required></td></tr> <tr class="required"><th><label class="required" for="id_is_cool">Is cool:</label></th> <td><select name="is_cool" id="id_is_cool"> -<option value="1" selected>Unknown</option> -<option value="2">Yes</option> -<option value="3">No</option> +<option value="unknown" selected>Unknown</option> +<option value="true">Yes</option> +<option value="false">No</option> </select></td></tr> <tr><th><label for="id_email">Email:</label></th><td> <input type="email" name="email" id="id_email"></td></tr> diff --git a/tests/forms_tests/widget_tests/test_nullbooleanselect.py b/tests/forms_tests/widget_tests/test_nullbooleanselect.py index 779a88893f..a732e86da4 100644 --- a/tests/forms_tests/widget_tests/test_nullbooleanselect.py +++ b/tests/forms_tests/widget_tests/test_nullbooleanselect.py @@ -11,36 +11,81 @@ class NullBooleanSelectTest(WidgetTest): def test_render_true(self): self.check_html(self.widget, 'is_cool', True, html=( """<select name="is_cool"> - <option value="1">Unknown</option> - <option value="2" selected>Yes</option> - <option value="3">No</option> + <option value="unknown">Unknown</option> + <option value="true" selected>Yes</option> + <option value="false">No</option> </select>""" )) def test_render_false(self): self.check_html(self.widget, 'is_cool', False, html=( """<select name="is_cool"> - <option value="1">Unknown</option> - <option value="2">Yes</option> - <option value="3" selected>No</option> + <option value="unknown">Unknown</option> + <option value="true">Yes</option> + <option value="false" selected>No</option> </select>""" )) def test_render_none(self): self.check_html(self.widget, 'is_cool', None, html=( """<select name="is_cool"> - <option value="1" selected>Unknown</option> - <option value="2">Yes</option> - <option value="3">No</option> + <option value="unknown" selected>Unknown</option> + <option value="true">Yes</option> + <option value="false">No</option> </select>""" )) - def test_render_value(self): + def test_render_value_unknown(self): + self.check_html(self.widget, 'is_cool', 'unknown', html=( + """<select name="is_cool"> + <option value="unknown" selected>Unknown</option> + <option value="true">Yes</option> + <option value="false">No</option> + </select>""" + )) + + def test_render_value_true(self): + self.check_html(self.widget, 'is_cool', 'true', html=( + """<select name="is_cool"> + <option value="unknown">Unknown</option> + <option value="true" selected>Yes</option> + <option value="false">No</option> + </select>""" + )) + + def test_render_value_false(self): + self.check_html(self.widget, 'is_cool', 'false', html=( + """<select name="is_cool"> + <option value="unknown">Unknown</option> + <option value="true">Yes</option> + <option value="false" selected>No</option> + </select>""" + )) + + def test_render_value_1(self): + self.check_html(self.widget, 'is_cool', '1', html=( + """<select name="is_cool"> + <option value="unknown" selected>Unknown</option> + <option value="true">Yes</option> + <option value="false">No</option> + </select>""" + )) + + def test_render_value_2(self): self.check_html(self.widget, 'is_cool', '2', html=( """<select name="is_cool"> - <option value="1">Unknown</option> - <option value="2" selected>Yes</option> - <option value="3">No</option> + <option value="unknown">Unknown</option> + <option value="true" selected>Yes</option> + <option value="false">No</option> + </select>""" + )) + + def test_render_value_3(self): + self.check_html(self.widget, 'is_cool', '3', html=( + """<select name="is_cool"> + <option value="unknown">Unknown</option> + <option value="true">Yes</option> + <option value="false" selected>No</option> </select>""" )) @@ -55,9 +100,9 @@ class NullBooleanSelectTest(WidgetTest): self.check_html(widget, 'id_bool', True, html=( """ <select name="id_bool"> - <option value="1">Unbekannt</option> - <option value="2" selected>Ja</option> - <option value="3">Nein</option> + <option value="unknown">Unbekannt</option> + <option value="true" selected>Ja</option> + <option value="false">Nein</option> </select> """ ))