From 63c56cda133a85a158502891c40465bc0331d3d9 Mon Sep 17 00:00:00 2001 From: Annabelle Wiegart Date: Sun, 18 Jan 2026 20:03:28 +0100 Subject: Fixed #35870 -- Made blank choice label in forms more accessible. Added new constant django.db.models.fields.BLANK_CHOICE_LABEL for an accessible and translatable blank choice label in forms. Deprecated django.db.models.fields.BLANK_CHOICE_DASH constant. Added the immediately deprecated transitional setting USE_BLANK_CHOICE_DASH. Co-Authored-By: Marijke Luttekes --- .../field_tests/test_typedchoicefield.py | 7 +++- tests/forms_tests/locale/de/LC_MESSAGES/django.mo | Bin 0 -> 322 bytes tests/forms_tests/locale/de/LC_MESSAGES/django.po | 14 ++++++++ tests/forms_tests/locale/de/__init__.py | 0 tests/forms_tests/tests/test_forms.py | 37 +++++++++++++++++++- tests/forms_tests/tests/test_i18n.py | 38 +++++++++++++++++++++ tests/forms_tests/widget_tests/test_radioselect.py | 19 +++++++---- 7 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 tests/forms_tests/locale/de/LC_MESSAGES/django.mo create mode 100644 tests/forms_tests/locale/de/LC_MESSAGES/django.po create mode 100644 tests/forms_tests/locale/de/__init__.py (limited to 'tests/forms_tests') diff --git a/tests/forms_tests/field_tests/test_typedchoicefield.py b/tests/forms_tests/field_tests/test_typedchoicefield.py index 3537623272..fe5cbe12ee 100644 --- a/tests/forms_tests/field_tests/test_typedchoicefield.py +++ b/tests/forms_tests/field_tests/test_typedchoicefield.py @@ -1,6 +1,7 @@ import decimal from django.core.exceptions import ValidationError +from django.db.models.utils import get_blank_choice_label from django.forms import TypedChoiceField from django.test import SimpleTestCase @@ -62,7 +63,11 @@ class TypedChoiceFieldTest(SimpleTestCase): self.assertFalse(f.has_changed("1", "1")) f = TypedChoiceField( - choices=[("", "---------"), ("a", "a"), ("b", "b")], + choices=[ + ("", get_blank_choice_label()), + ("a", "a"), + ("b", "b"), + ], coerce=str, required=False, initial=None, diff --git a/tests/forms_tests/locale/de/LC_MESSAGES/django.mo b/tests/forms_tests/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000000..f2695ea426 Binary files /dev/null and b/tests/forms_tests/locale/de/LC_MESSAGES/django.mo differ diff --git a/tests/forms_tests/locale/de/LC_MESSAGES/django.po b/tests/forms_tests/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000000..e118e28d98 --- /dev/null +++ b/tests/forms_tests/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,14 @@ +# This file is distributed under the same license as the Django package. +# +msgid "" +msgstr "" +"Project-Id-Version: django\n" +"Report-Msgid-Bugs-To: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "- Select an option -" +msgstr "- Wähle eine Option -" diff --git a/tests/forms_tests/locale/de/__init__.py b/tests/forms_tests/locale/de/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py index 14fbc19a59..2c1ec78650 100644 --- a/tests/forms_tests/tests/test_forms.py +++ b/tests/forms_tests/tests/test_forms.py @@ -1,11 +1,14 @@ import copy import datetime import json +import textwrap import uuid +from django.conf import USE_BLANK_CHOICE_DASH_DEPRECATED_MSG from django.core.exceptions import NON_FIELD_ERRORS from django.core.files.uploadedfile import SimpleUploadedFile from django.core.validators import MaxValueValidator, RegexValidator +from django.db.models.utils import get_blank_choice_label from django.forms import ( BooleanField, BoundField, @@ -42,9 +45,10 @@ from django.forms.renderers import DjangoTemplates, get_default_renderer from django.forms.utils import ErrorDict, ErrorList from django.http import QueryDict from django.template import Context, Template -from django.test import SimpleTestCase +from django.test import SimpleTestCase, ignore_warnings from django.test.utils import override_settings from django.utils.datastructures import MultiValueDict +from django.utils.deprecation import RemovedInDjango70Warning from django.utils.safestring import mark_safe from . import jinja2_tests @@ -797,6 +801,37 @@ aria-describedby="id_birthday_error"> """, ) + # RemovedInDjango70Warning + @ignore_warnings(category=RemovedInDjango70Warning) + @override_settings(USE_BLANK_CHOICE_DASH=True) + def test_blank_choice_dash(self): + class SomeForm(Form): + somechoices = ChoiceField( + choices=( + ("0", get_blank_choice_label()), + ("1", "Test 1"), + ("2", "Test 2"), + ) + ) + + f = SomeForm() + + self.assertHTMLEqual( + f.as_p(), + textwrap.dedent(""" +

+ + +

"""), + ) + + self.assertWarnsMessage( + RemovedInDjango70Warning, USE_BLANK_CHOICE_DASH_DEPRECATED_MSG + ) + def test_forms_with_radio(self): # Add widget=RadioSelect to use that widget with a ChoiceField. f = FrameworkForm(auto_id=False) diff --git a/tests/forms_tests/tests/test_i18n.py b/tests/forms_tests/tests/test_i18n.py index 5b8d92c0f6..fe6a67dd16 100644 --- a/tests/forms_tests/tests/test_i18n.py +++ b/tests/forms_tests/tests/test_i18n.py @@ -1,3 +1,6 @@ +import textwrap + +from django.db.models.utils import get_blank_choice_label from django.forms import ( CharField, ChoiceField, @@ -122,6 +125,41 @@ class FormsI18nTests(SimpleTestCase): CopyForm() + def test_blank_choice_label(self): + class SomeForm(Form): + somechoices = ChoiceField( + choices=( + ("0", get_blank_choice_label()), + ("1", "Test 1"), + ("2", "Test 2"), + ) + ) + + f = SomeForm() + + self.assertHTMLEqual( + f.as_p(), + textwrap.dedent(""" +

+ + +

"""), + ) + + with translation.override("de"): + self.assertHTMLEqual( + f.as_p(), + textwrap.dedent("""

+ +

"""), + ) + @jinja2_tests class Jinja2FormsI18nTests(FormsI18nTests): diff --git a/tests/forms_tests/widget_tests/test_radioselect.py b/tests/forms_tests/widget_tests/test_radioselect.py index be336151ef..a4d4919009 100644 --- a/tests/forms_tests/widget_tests/test_radioselect.py +++ b/tests/forms_tests/widget_tests/test_radioselect.py @@ -1,12 +1,13 @@ import datetime +from django.db.models.utils import get_blank_choice_label from django.forms import ChoiceField, Form, MultiWidget, RadioSelect, TextInput from django.test import override_settings from django.utils.safestring import mark_safe from .test_choicewidget import ChoiceWidgetTest -BLANK_CHOICE_DASH = (("", "------"),) +BLANK_CHOICE = (("", get_blank_choice_label()),) class RadioSelectTest(ChoiceWidgetTest): @@ -16,7 +17,9 @@ class RadioSelectTest(ChoiceWidgetTest): html = """
- +
@@ -32,7 +35,7 @@ class RadioSelectTest(ChoiceWidgetTest):
""" - beatles_with_blank = BLANK_CHOICE_DASH + self.beatles + beatles_with_blank = BLANK_CHOICE + self.beatles for choices in (beatles_with_blank, dict(beatles_with_blank)): with self.subTest(choices): self.check_html(self.widget(choices=choices), "beatle", "J", html=html) @@ -83,11 +86,13 @@ class RadioSelectTest(ChoiceWidgetTest): """ If value is None, none of the options are selected. """ - choices = BLANK_CHOICE_DASH + self.beatles + choices = BLANK_CHOICE + self.beatles html = """
- +
@@ -463,11 +468,11 @@ class RadioSelectTest(ChoiceWidgetTest): def test_render_as_subwidget(self): """A RadioSelect as a subwidget of MultiWidget.""" - choices = BLANK_CHOICE_DASH + self.beatles + choices = BLANK_CHOICE + self.beatles html = """
+ - Select an option -
-- cgit v1.3