1
0
mirror of https://github.com/django/django.git synced 2024-12-22 17:16:24 +00:00

Fixed #35870 -- Replaced blank select choice with accessible text.

Thanks James Scholes for providing feedback on accessible options, Thibaud Colas for running screen reader tests, Carlton Gibson for helping get started.
This commit is contained in:
Marijke Luttekes 2024-12-17 00:22:56 +01:00
parent 3ee4c6a27a
commit b3505f38f6
No known key found for this signature in database
GPG Key ID: B8079A753CA344D1
21 changed files with 120 additions and 64 deletions

View File

@ -223,6 +223,9 @@ FORM_RENDERER = "django.forms.renderers.DjangoTemplates"
# Set to True to assume "https" during the Django 5.x release cycle.
FORMS_URLFIELD_ASSUME_HTTPS = False
# ToDo: Documentation string
FORMS_DEFAULT_BLANK_CHOICE_LABEL = gettext_noop("- Select an option -")
# Default email address to use for various automated correspondence from
# the site managers.
DEFAULT_FROM_EMAIL = "webmaster@localhost"

View File

@ -1087,11 +1087,13 @@ class ModelAdmin(BaseModelAdmin):
actions = self._filter_actions_by_permissions(request, self._get_base_actions())
return {name: (func, name, desc) for func, name, desc in actions}
def get_action_choices(self, request, default_choices=models.BLANK_CHOICE_DASH):
def get_action_choices(self, request, default_choices=None):
"""
Return a list of choices for use in a form object. Each choice is a
tuple (name, description).
"""
if default_choices is None:
default_choices = [("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)]
choices = [] + default_choices
for func, name, description in self.get_actions(request).values():
choice = (name, description % model_format_dict(self.opts))

View File

@ -82,6 +82,7 @@ class NOT_PROVIDED:
# The values to use for "blank" in SelectFields. Will be appended to the start
# of most "choices" lists.
# ToDo: write deprecation notice per Trac issue #35870.
BLANK_CHOICE_DASH = [("", "---------")]
@ -1056,7 +1057,7 @@ class Field(RegisterLookupMixin):
def get_choices(
self,
include_blank=True,
blank_choice=BLANK_CHOICE_DASH,
blank_choice=None,
limit_choices_to=None,
ordering=(),
):
@ -1064,6 +1065,8 @@ class Field(RegisterLookupMixin):
Return choices with a default blank choices included, for use
as <select> choices for this field.
"""
if blank_choice is None:
blank_choice = [("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)]
if self.choices is not None:
if include_blank:
return BlankChoiceIterator(self.choices, blank_choice)

View File

@ -11,12 +11,12 @@ they're the closest concept currently available.
import warnings
from django.conf import settings
from django.core import exceptions
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.functional import cached_property
from django.utils.hashable import make_hashable
from . import BLANK_CHOICE_DASH
from .mixins import FieldCacheMixin
@ -175,7 +175,7 @@ class ForeignObjectRel(FieldCacheMixin):
def get_choices(
self,
include_blank=True,
blank_choice=BLANK_CHOICE_DASH,
blank_choice=None,
limit_choices_to=None,
ordering=(),
):
@ -186,6 +186,8 @@ class ForeignObjectRel(FieldCacheMixin):
Analog of django.db.models.fields.Field.get_choices(), provided
initially for utilization by RelatedFieldListFilter.
"""
if blank_choice is None:
blank_choice = [("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)]
limit_choices_to = limit_choices_to or self.limit_choices_to
qs = self.related_model._default_manager.complex_filter(limit_choices_to)
if ordering:

View File

@ -1217,7 +1217,7 @@ class FilePathField(ChoiceField):
if self.required:
self.choices = []
else:
self.choices = [("", "---------")]
self.choices = [("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)]
if self.match is not None:
self.match_re = re.compile(self.match)

View File

@ -5,6 +5,7 @@ and database field objects.
from itertools import chain
from django.conf import settings
from django.core.exceptions import (
NON_FIELD_ERRORS,
FieldError,
@ -1456,7 +1457,7 @@ class ModelChoiceField(ChoiceField):
self,
queryset,
*,
empty_label="---------",
empty_label="",
required=True,
widget=None,
label=None,
@ -1483,6 +1484,8 @@ class ModelChoiceField(ChoiceField):
):
self.empty_label = None
else:
if empty_label == "":
empty_label = settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL
self.empty_label = empty_label
self.queryset = queryset
self.limit_choices_to = limit_choices_to # limit the queryset later.

View File

@ -1376,7 +1376,7 @@ generating choices. See :ref:`iterating-relationship-choices` for details.
By default the ``<select>`` widget used by ``ModelChoiceField`` will have an
empty choice at the top of the list. You can change the text of this
label (which is ``"---------"`` by default) with the ``empty_label``
label (which is ``"- Select an option -"`` by default) with the ``empty_label``
attribute, or you can disable the empty label entirely by setting
``empty_label`` to ``None``::

View File

@ -234,7 +234,7 @@ documentation.
.. _field-choices-blank-label:
Unless :attr:`blank=False<Field.blank>` is set on the field along with a
:attr:`~Field.default` then a label containing ``"---------"`` will be rendered
:attr:`~Field.default` then a label containing ``"- Select an option -"`` will be rendered
with the select box. To override this behavior, add a tuple to ``choices``
containing ``None``; e.g. ``(None, 'Your String For Display')``.
Alternatively, you can use an empty string instead of ``None`` where this makes

View File

@ -1697,6 +1697,16 @@ Set this transitional setting to ``True`` to opt into using ``"https"`` as the
new default value of :attr:`URLField.assume_scheme
<django.forms.URLField.assume_scheme>` during the Django 5.x release cycle.
.. setting:: FORMS_DEFAULT_BLANK_CHOICE_LABEL
``FORMS_DEFAULT_BLANK_CHOICE_LABEL``
------------------------------------
Default: ``'- Select an option -'`` (Translated)
The default label for the blank choice option used by ``<select>`` elements in forms.
This value can be a regular or a translatable string, and can be overriden individually per form.
.. setting:: FORMAT_MODULE_PATH
``FORMAT_MODULE_PATH``

View File

@ -294,7 +294,7 @@ class AdminActionsTest(TestCase):
self.assertContains(
response,
"""<label>Action: <select name="action" required>
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="delete_selected">Delete selected external
subscribers</option>
<option value="redirect_to">Redirect to (Awesome action)</option>

View File

@ -110,7 +110,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertHTMLEqual(
fk_dropdown.get_attribute("innerHTML"),
f"""
<option value="" selected="">---------</option>
<option value="" selected="">- Select an option -</option>
<option value="{id_value}" selected>{interesting_name}</option>
""",
)
@ -155,7 +155,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertHTMLEqual(
fk_dropdown.get_attribute("innerHTML"),
f"""
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="{id_value}">{name}</option>
""",
)

View File

@ -7,6 +7,7 @@ from unittest import mock
from urllib.parse import parse_qsl, urljoin, urlsplit
from django import forms
from django.conf import settings
from django.contrib import admin
from django.contrib.admin import AdminSite, ModelAdmin
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
@ -6450,7 +6451,9 @@ class SeleniumTests(AdminSeleniumTestCase):
self.selenium.switch_to.window(self.selenium.window_handles[0])
select = Select(self.selenium.find_element(By.ID, "id_parent"))
self.assertEqual(ParentWithUUIDPK.objects.count(), 0)
self.assertEqual(select.first_selected_option.text, "---------")
self.assertEqual(
select.first_selected_option.text, settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL
)
self.assertEqual(select.first_selected_option.get_attribute("value"), "")
def test_inline_with_popup_cancel_delete(self):
@ -6696,7 +6699,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertHTMLEqual(
_get_HTML_inside_element_by_id(born_country_select_id),
"""
<option value="" selected="">---------</option>
<option value="" selected="">- Select an option -</option>
<option value="1" selected="">Argentina</option>
""",
)
@ -6715,7 +6718,7 @@ class SeleniumTests(AdminSeleniumTestCase):
# limit_choices_to.
self.assertHTMLEqual(
_get_HTML_inside_element_by_id(favorite_country_to_vacation_select_id),
'<option value="" selected="">---------</option>',
'<option value="" selected="">- Select an option -</option>',
)
# Add new Country from the living_country select.
@ -6734,7 +6737,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertHTMLEqual(
_get_HTML_inside_element_by_id(born_country_select_id),
"""
<option value="" selected="">---------</option>
<option value="" selected="">- Select an option -</option>
<option value="1" selected="">Argentina</option>
<option value="2">Spain</option>
""",
@ -6756,7 +6759,7 @@ class SeleniumTests(AdminSeleniumTestCase):
# limit_choices_to.
self.assertHTMLEqual(
_get_HTML_inside_element_by_id(favorite_country_to_vacation_select_id),
'<option value="" selected="">---------</option>',
'<option value="" selected="">- Select an option -</option>',
)
# Edit second Country created from living_country select.
@ -6776,7 +6779,7 @@ class SeleniumTests(AdminSeleniumTestCase):
self.assertHTMLEqual(
_get_HTML_inside_element_by_id(born_country_select_id),
"""
<option value="" selected="">---------</option>
<option value="" selected="">- Select an option -</option>
<option value="1" selected="">Argentina</option>
<option value="2">Italy</option>
""",
@ -6796,7 +6799,7 @@ class SeleniumTests(AdminSeleniumTestCase):
# favorite_country_to_vacation field has no options.
self.assertHTMLEqual(
_get_HTML_inside_element_by_id(favorite_country_to_vacation_select_id),
'<option value="" selected="">---------</option>',
'<option value="" selected="">- Select an option -</option>',
)
# Add a new Asian country.

View File

@ -1811,7 +1811,7 @@ class RelatedFieldWidgetSeleniumTests(AdminWidgetSeleniumTestCase):
# Chrome and Safari don't update related object links when selecting
# the same option as previously submitted. As a consequence, the
# "pencil" and "eye" buttons remain disable, so select "---------"
# "pencil" and "eye" buttons remain disable, so select "- Select an option -"
# first.
select = Select(self.selenium.find_element(By.ID, "id_user"))
select.select_by_index(0)

View File

@ -1,5 +1,6 @@
import decimal
from django.conf import settings
from django.core.exceptions import ValidationError
from django.forms import TypedChoiceField
from django.test import SimpleTestCase
@ -59,7 +60,11 @@ class TypedChoiceFieldTest(SimpleTestCase):
self.assertFalse(f.has_changed("1", "1"))
f = TypedChoiceField(
choices=[("", "---------"), ("a", "a"), ("b", "b")],
choices=[
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
("a", "a"),
("b", "b"),
],
coerce=str,
required=False,
initial=None,

View File

@ -1,12 +1,13 @@
import datetime
from django.conf import settings
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 = (("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),)
class RadioSelectTest(ChoiceWidgetTest):
@ -16,7 +17,9 @@ class RadioSelectTest(ChoiceWidgetTest):
html = """
<div>
<div>
<label><input type="radio" name="beatle" value="">------</label>
<label>
<input type="radio" name="beatle" value="">- Select an option -
</label>
</div>
<div>
<label><input checked type="radio" name="beatle" value="J">John</label>
@ -32,7 +35,7 @@ class RadioSelectTest(ChoiceWidgetTest):
</div>
</div>
"""
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 = """
<div>
<div>
<label><input checked type="radio" name="beatle" value="">------</label>
<label>
<input checked type="radio" name="beatle" value="">- Select an option -
</label>
</div>
<div>
<label><input type="radio" name="beatle" value="J">John</label>
@ -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 = """
<div>
<div><label>
<input type="radio" name="beatle_0" value="">------</label>
<input type="radio" name="beatle_0" value="">- Select an option -</label>
</div>
<div><label>
<input checked type="radio" name="beatle_0" value="J">John</label>

View File

@ -1,4 +1,5 @@
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import IntegrityError, models, transaction
from django.test import SimpleTestCase, TestCase
@ -48,7 +49,10 @@ class BooleanFieldTests(TestCase):
"""
choices = [(1, "Si"), (2, "No")]
f = models.BooleanField(choices=choices)
self.assertEqual(f.formfield().choices, [("", "---------")] + choices)
self.assertEqual(
f.formfield().choices,
[("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)] + choices,
)
def test_nullbooleanfield_formfield(self):
f = models.BooleanField(null=True)

View File

@ -1,6 +1,7 @@
import pickle
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.test import SimpleTestCase, TestCase
@ -354,7 +355,10 @@ class GetChoicesTests(SimpleTestCase):
def test_lazy_strings_not_evaluated(self):
lazy_func = lazy(lambda x: 0 / 0, int) # raises ZeroDivisionError if evaluated.
f = models.CharField(choices=[(lazy_func("group"), [("a", "A"), ("b", "B")])])
self.assertEqual(f.get_choices(include_blank=True)[0], ("", "---------"))
self.assertEqual(
f.get_choices(include_blank=True)[0],
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
)
class GetChoicesOrderingTests(TestCase):

View File

@ -1,6 +1,7 @@
import datetime
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.forms.models import ModelChoiceIterator, ModelChoiceIteratorValue
from django.forms.widgets import CheckboxSelectMultiple
@ -24,7 +25,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertEqual(
list(f.choices),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(self.c1.pk, "Entertainment"),
(self.c2.pk, "A test"),
(self.c3.pk, "Third"),
@ -102,7 +103,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertEqual(
list(f.choices),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(self.c1.pk, "Entertainment"),
(self.c2.pk, "A test"),
],
@ -118,7 +119,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertEqual(
list(gen_two),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(self.c1.pk, "Entertainment"),
(self.c2.pk, "A test"),
],
@ -130,7 +131,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertEqual(
list(f.choices),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(self.c1.pk, "category Entertainment"),
(self.c2.pk, "category A test"),
(self.c3.pk, "category Third"),
@ -143,7 +144,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertEqual(
list(f.choices),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(self.c1.pk, "Entertainment"),
(self.c2.pk, "A test"),
(self.c3.pk, "Third"),
@ -154,7 +155,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertEqual(
list(f.choices),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(self.c1.pk, "Entertainment"),
(self.c2.pk, "A test"),
(self.c3.pk, "Third"),
@ -174,6 +175,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertIs(bool(f.choices), True)
def test_choices_radio_blank(self):
blank_choice = [("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)]
choices = [
(self.c1.pk, "Entertainment"),
(self.c2.pk, "A test"),
@ -190,7 +192,7 @@ class ModelChoiceFieldTests(TestCase):
)
self.assertEqual(
list(f.choices),
[("", "---------")] + choices if blank else choices,
(blank_choice + choices if blank else choices),
)
def test_deepcopies_widget(self):
@ -425,7 +427,7 @@ class ModelChoiceFieldTests(TestCase):
self.assertCountEqual(
list(f.choices),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(self.c1.pk, "Entertainment"),
(self.c2.pk, "A test"),
(self.c3.pk, "Third"),

View File

@ -5,6 +5,7 @@ from decimal import Decimal
from unittest import mock, skipUnless
from django import forms
from django.conf import settings
from django.core.exceptions import (
NON_FIELD_ERRORS,
FieldError,
@ -341,7 +342,7 @@ class ModelFormBaseTest(TestCase):
self.assertEqual(
list(form.fields["author"].choices),
[
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(writer.pk, "Joe Doe"),
],
)
@ -1534,7 +1535,7 @@ class ModelFormBasicTests(TestCase):
<li>Slug: <input type="text" name="slug" maxlength="50" required></li>
<li>Pub date: <input type="text" name="pub_date" required></li>
<li>Writer: <select name="writer" required>
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="%s">Bob Woodward</option>
<option value="%s">Mike Royko</option>
</select></li>
@ -1546,7 +1547,7 @@ class ModelFormBasicTests(TestCase):
<option value="%s">Third test</option>
</select></li>
<li>Status: <select name="status">
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="1">Draft</option>
<option value="2">Pending</option>
<option value="3">Live</option>
@ -1588,7 +1589,7 @@ class ModelFormBasicTests(TestCase):
<li>Pub date:
<input type="text" name="pub_date" value="1988-01-04" required></li>
<li>Writer: <select name="writer" required>
<option value="">---------</option>
<option value="">- Select an option -</option>
<option value="%s">Bob Woodward</option>
<option value="%s" selected>Mike Royko</option>
</select></li>
@ -1600,7 +1601,7 @@ class ModelFormBasicTests(TestCase):
<option value="%s">Third test</option>
</select></li>
<li>Status: <select name="status">
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="1">Draft</option>
<option value="2">Pending</option>
<option value="3">Live</option>
@ -1733,7 +1734,7 @@ class ModelFormBasicTests(TestCase):
</div>
<div>Writer:
<select name="writer" required>
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="%s">Bob Woodward</option>
<option value="%s">Mike Royko</option>
</select>
@ -1750,7 +1751,7 @@ class ModelFormBasicTests(TestCase):
</div>
<div>Status:
<select name="status">
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="1">Draft</option><option value="2">Pending</option>
<option value="3">Live</option>
</select>
@ -1783,7 +1784,7 @@ class ModelFormBasicTests(TestCase):
<li>Pub date:
<input type="text" name="pub_date" value="1988-01-04" required></li>
<li>Writer: <select name="writer" required>
<option value="">---------</option>
<option value="">- Select an option -</option>
<option value="%s">Bob Woodward</option>
<option value="%s" selected>Mike Royko</option>
</select></li>
@ -1795,7 +1796,7 @@ class ModelFormBasicTests(TestCase):
<option value="%s">Third test</option>
</select></li>
<li>Status: <select name="status">
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="1">Draft</option>
<option value="2">Pending</option>
<option value="3">Live</option>
@ -1957,7 +1958,7 @@ class ModelFormBasicTests(TestCase):
'<li>Slug: <input type="text" name="slug" maxlength="50" required></li>'
'<li>Pub date: <input type="text" name="pub_date" required></li>'
'<li>Writer: <select name="writer" required>'
'<option value="" selected>---------</option>'
'<option value="" selected>- Select an option -</option>'
'<option value="%s">Bob Woodward</option>'
'<option value="%s">Mike Royko</option>'
"</select></li>"
@ -1969,7 +1970,7 @@ class ModelFormBasicTests(TestCase):
'<option value="%s">Third test</option>'
"</select> </li>"
'<li>Status: <select name="status">'
'<option value="" selected>---------</option>'
'<option value="" selected>- Select an option -</option>'
'<option value="1">Draft</option>'
'<option value="2">Pending</option>'
'<option value="3">Live</option>'
@ -1986,7 +1987,7 @@ class ModelFormBasicTests(TestCase):
'<li>Slug: <input type="text" name="slug" maxlength="50" required></li>'
'<li>Pub date: <input type="text" name="pub_date" required></li>'
'<li>Writer: <select name="writer" required>'
'<option value="" selected>---------</option>'
'<option value="" selected>- Select an option -</option>'
'<option value="%s">Bob Woodward</option>'
'<option value="%s">Carl Bernstein</option>'
'<option value="%s">Mike Royko</option>'
@ -2000,7 +2001,7 @@ class ModelFormBasicTests(TestCase):
'<option value="%s">Fourth</option>'
"</select></li>"
'<li>Status: <select name="status">'
'<option value="" selected>---------</option>'
'<option value="" selected>- Select an option -</option>'
'<option value="1">Draft</option>'
'<option value="2">Pending</option>'
'<option value="3">Live</option>'
@ -2044,7 +2045,8 @@ class ModelFormBasicTests(TestCase):
self.assertEqual(call_count, 0)
self.assertEqual(
form.fields["animal"].choices,
models.BLANK_CHOICE_DASH + [("LION", "Lion"), ("ZEBRA", "Zebra")],
[("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)]
+ [("LION", "Lion"), ("ZEBRA", "Zebra")],
)
self.assertEqual(call_count, 1)
@ -2410,7 +2412,7 @@ class ModelOneToOneFieldTests(TestCase):
"""
<p><label for="id_writer">Writer:</label>
<select name="writer" id="id_writer" required>
<option value="" selected>---------</option>
<option value="" selected>- Select an option -</option>
<option value="%s">Bob Woodward</option>
<option value="%s">Mike Royko</option>
</select></p>
@ -2437,7 +2439,7 @@ class ModelOneToOneFieldTests(TestCase):
"""
<p><label for="id_writer">Writer:</label>
<select name="writer" id="id_writer" required>
<option value="">---------</option>
<option value="">- Select an option -</option>
<option value="%s" selected>Bob Woodward</option>
<option value="%s">Mike Royko</option>
</select></p>
@ -2726,7 +2728,8 @@ class FileAndImageFieldTests(TestCase):
form = FPForm()
self.assertEqual(
[name for _, name in form["path"].field.choices], ["---------", "models.py"]
[name for _, name in form["path"].field.choices],
[settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL, "models.py"],
)
@skipUnless(test_images, "Pillow not installed")
@ -3055,7 +3058,7 @@ class OtherModelFormTests(TestCase):
self.assertEqual(
tuple(field.choices),
(
("", "---------"),
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(multicolor_item.pk, "blue, red"),
(red_item.pk, "red"),
),
@ -3069,14 +3072,19 @@ class OtherModelFormTests(TestCase):
field = forms.ModelChoiceField(Inventory.objects.all(), to_field_name="barcode")
self.assertEqual(
tuple(field.choices),
(("", "---------"), (86, "Apple"), (87, "Core"), (22, "Pear")),
(
("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL),
(86, "Apple"),
(87, "Core"),
(22, "Pear"),
),
)
form = InventoryForm(instance=core)
self.assertHTMLEqual(
str(form["parent"]),
"""<select name="parent" id="id_parent">
<option value="">---------</option>
<option value="">- Select an option -</option>
<option value="86" selected>Apple</option>
<option value="87">Core</option>
<option value="22">Pear</option>

View File

@ -1267,7 +1267,7 @@ class ModelFormsetTest(TestCase):
formset.forms[0].as_p(),
'<p><label for="id_form-0-owner">Owner:</label>'
'<select name="form-0-owner" id="id_form-0-owner">'
'<option value="" selected>---------</option>'
'<option value="" selected>- Select an option -</option>'
'<option value="%d">Joe Perry at Giordanos</option>'
'<option value="%d">Jack Berry at Giordanos</option>'
"</select></p>"

View File

@ -1,6 +1,7 @@
from datetime import date
from django import forms
from django.conf import settings
from django.contrib.admin.models import ADDITION, CHANGE, DELETION, LogEntry
from django.contrib.admin.options import (
HORIZONTAL,
@ -664,7 +665,7 @@ class ModelAdminTests(TestCase):
'<div class="related-widget-wrapper" data-model-ref="band">'
'<select data-context="available-source" '
'name="main_band" id="id_main_band" required>'
'<option value="" selected>---------</option>'
'<option value="" selected>- Select an option -</option>'
'<option value="%d">The Beatles</option>'
'<option value="%d">The Doors</option>'
"</select></div>" % (band2.id, self.band.id),
@ -688,7 +689,7 @@ class ModelAdminTests(TestCase):
'<div class="related-widget-wrapper" data-model-ref="band">'
'<select data-context="available-source" '
'name="main_band" id="id_main_band" required>'
'<option value="" selected>---------</option>'
'<option value="" selected>- Select an option -</option>'
'<option value="%d">The Doors</option>'
"</select></div>" % self.band.id,
)
@ -736,29 +737,30 @@ class ModelAdminTests(TestCase):
# ForeignKey widgets in the admin are wrapped with RelatedFieldWidgetWrapper so
# they need to be handled properly when type checking. For Select fields, all of
# the choices lists have a first entry of dashes.
blank_option = ("", settings.FORMS_DEFAULT_BLANK_CHOICE_LABEL)
cma = ModelAdmin(Concert, self.site)
cmafa = cma.get_form(request)
self.assertEqual(type(cmafa.base_fields["main_band"].widget.widget), Select)
self.assertEqual(
list(cmafa.base_fields["main_band"].widget.choices),
[("", "---------"), (self.band.id, "The Doors")],
[blank_option, (self.band.id, "The Doors")],
)
self.assertEqual(type(cmafa.base_fields["opening_band"].widget.widget), Select)
self.assertEqual(
list(cmafa.base_fields["opening_band"].widget.choices),
[("", "---------"), (self.band.id, "The Doors")],
[blank_option, (self.band.id, "The Doors")],
)
self.assertEqual(type(cmafa.base_fields["day"].widget), Select)
self.assertEqual(
list(cmafa.base_fields["day"].widget.choices),
[("", "---------"), (1, "Fri"), (2, "Sat")],
[blank_option, (1, "Fri"), (2, "Sat")],
)
self.assertEqual(type(cmafa.base_fields["transport"].widget), Select)
self.assertEqual(
list(cmafa.base_fields["transport"].widget.choices),
[("", "---------"), (1, "Plane"), (2, "Train"), (3, "Bus")],
[blank_option, (1, "Plane"), (2, "Train"), (3, "Bus")],
)
def test_foreign_key_as_radio_field(self):