From 6986f79643282cb77d9c17e2044d8296ecf2c798 Mon Sep 17 00:00:00 2001
From: mondi04 <206898989+mondi04@users.noreply.github.com>
Date: Sat, 6 Jun 2026 22:35:55 +0200
Subject: [PATCH 1/2] feat: add disabled support to SelectChoice and Select
widget
---
src/wtforms/fields/choices.py | 3 +++
src/wtforms/widgets/core.py | 2 ++
tests/fields/test_select.py | 23 +++++++++++++++++++----
3 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/src/wtforms/fields/choices.py b/src/wtforms/fields/choices.py
index 66a9719d..0682d30e 100644
--- a/src/wtforms/fields/choices.py
+++ b/src/wtforms/fields/choices.py
@@ -59,6 +59,7 @@ class Choice(NamedTuple):
label: str
selected: bool
render_kw: dict
+ disabled: bool = False
@dataclass
@@ -82,6 +83,7 @@ class SelectChoice:
label: str = None # type: ignore[assignment]
render_kw: dict = field(default_factory=dict)
optgroup: str | None = None
+ disabled: bool = False
def __post_init__(self):
if self.label is None:
@@ -365,6 +367,7 @@ def iter_choices(self):
label=c.label,
selected=self.coerce(c.value) == self.data,
render_kw=c.render_kw,
+ disabled=c.disabled,
)
for c in choices
]
diff --git a/src/wtforms/widgets/core.py b/src/wtforms/widgets/core.py
index 9b95bd0e..fb125825 100644
--- a/src/wtforms/widgets/core.py
+++ b/src/wtforms/widgets/core.py
@@ -448,6 +448,8 @@ def render_option(cls, choice, **kwargs):
options = {"value": value, **(choice.render_kw or {}), **kwargs}
if choice.selected:
options["selected"] = True
+ if choice.disabled:
+ options["disabled"] = True
label = escape(choice.label or choice.value)
return Markup(f"")
diff --git a/tests/fields/test_select.py b/tests/fields/test_select.py
index 7618118c..e14606f8 100644
--- a/tests/fields/test_select.py
+++ b/tests/fields/test_select.py
@@ -445,8 +445,8 @@ def test_iter_groups_items_unpack_as_3_2_tuples():
)
form = F(a="a")
for _label, items in form.a.iter_groups():
- for value, label, selected, render_kw in items:
- assert (value, label, selected, render_kw) == ("a", "Foo", True, {})
+ for value, label, selected, render_kw, disabled in items:
+ assert (value, label, selected, render_kw, disabled) == ("a", "Foo", True, {}, False)
def test_dict_str_str_flat_choices():
@@ -707,8 +707,8 @@ def test_iter_choices_tuple_unpacking():
a=SelectField(choices=[SelectChoice("a", "Foo"), SelectChoice("b", "Bar")])
)
form = F(a="a")
- unpacked = [(v, lab, sel, rk) for v, lab, sel, rk in form.a.iter_choices()]
- assert unpacked == [("a", "Foo", True, {}), ("b", "Bar", False, {})]
+ unpacked = [(v, lab, sel, rk, disabled) for v, lab, sel, rk, disabled in form.a.iter_choices()]
+ assert unpacked == [("a", "Foo", True, {}, False), ("b", "Bar", False, {}, False)]
def test_select_field_enum_renders_selected():
@@ -716,3 +716,18 @@ def test_select_field_enum_renders_selected():
F = make_form(a=SelectField(choices=SelectChoice.from_enum(_Plain), coerce=_Plain))
form = F(a=_Plain.GREEN)
assert '' in form.a()
+
+def test_disabled_option():
+ """SelectChoice supports disable=True, which renders the option with the disabled attribute."""
+ F = make_form(
+ a=SelectField(
+ choices=[
+ SelectChoice("a", "Foo"),
+ SelectChoice("b", "Bar", disabled=True),
+ ]
+ )
+ )
+ form = F(a="a")
+ html = form.a()
+ assert 'disabled' in html
+ assert html.count("disabled") == 1
\ No newline at end of file
From ba98f23f1983285b70b0d14da4cc954817583d3d Mon Sep 17 00:00:00 2001
From: mondi04 <206898989+mondi04@users.noreply.github.com>
Date: Sat, 6 Jun 2026 22:52:21 +0200
Subject: [PATCH 2/2] fix: apply ruff formatting and line length fixes
---
tests/fields/test_select.py | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)
diff --git a/tests/fields/test_select.py b/tests/fields/test_select.py
index e14606f8..a5ac0840 100644
--- a/tests/fields/test_select.py
+++ b/tests/fields/test_select.py
@@ -446,7 +446,13 @@ def test_iter_groups_items_unpack_as_3_2_tuples():
form = F(a="a")
for _label, items in form.a.iter_groups():
for value, label, selected, render_kw, disabled in items:
- assert (value, label, selected, render_kw, disabled) == ("a", "Foo", True, {}, False)
+ assert (value, label, selected, render_kw, disabled) == (
+ "a",
+ "Foo",
+ True,
+ {},
+ False,
+ )
def test_dict_str_str_flat_choices():
@@ -707,7 +713,10 @@ def test_iter_choices_tuple_unpacking():
a=SelectField(choices=[SelectChoice("a", "Foo"), SelectChoice("b", "Bar")])
)
form = F(a="a")
- unpacked = [(v, lab, sel, rk, disabled) for v, lab, sel, rk, disabled in form.a.iter_choices()]
+ unpacked = [
+ (v, lab, sel, rk, disabled)
+ for v, lab, sel, rk, disabled in form.a.iter_choices()
+ ]
assert unpacked == [("a", "Foo", True, {}, False), ("b", "Bar", False, {}, False)]
@@ -717,8 +726,9 @@ def test_select_field_enum_renders_selected():
form = F(a=_Plain.GREEN)
assert '' in form.a()
+
def test_disabled_option():
- """SelectChoice supports disable=True, which renders the option with the disabled attribute."""
+ """SelectChoice supports disable=True, rendering