Repository: wtforms/wtforms (python)

Issue:
Field render kwargs are not populated to the inputs of RadioField
In most cases you can control HTML attributes of the rendered fields via keyword arguments to the form field's `__call__` method. This doesn't work right for `RadioField` (or probably anything that uses `FieldList`, where the keyword arguments are consumed by the wrapping `<ul>` element, and _not_ passed on to the actual `<input>`s (or whatever the nested fields are):

```python
>>> from wtforms import Form
>>> from wtforms.fields import RadioField, SelectField
>>> class MyForm(Form):
...     a_or_b = SelectField(choices=[("A", "A"), ("B", "B")])
...
>>> f = MyForm()
>>> f.a_or_b(disabled=True)
'<select disabled id="a_or_b" name="a_or_b"><option value="A">A</option><option value="B">B</option></select>'
```
For `SelectField` (for instance), the `disabled` attribute is applied to the `<select>`.

```python
>>> class MyOtherForm(Form):
...     a_or_b = RadioField(choices=[("A", "A"), ("B", "B")])
...
>>> f = MyOtherForm()
>>> f.a_or_b(disabled=True)
'<ul disabled id="a_or_b"><li><input id="a_or_b-0" name="a_or_b" type="radio" value="A"> <label for="a_or_b-0">A</label></li><li><input id="a_or_b-1" name="a_or_b" type="radio" value="B"> <label for="a_or_b-1">B</label></li></ul>'
```
With `RadioField`, the `disabled` attribute is put on the `<ul>`.

I think this is slightly tricky to solve, since users might want this behavior in some cases (eg to style an element by passing the `class_` keyword argument). I'm willing to take a stab at a solution, but I'm not sure how best to approach it, so I'm open to ideas.

Pull request description:
RadioField renders `render_kw`
This pull request comes after the accidental closing of #628 and fixes #490. I cherry-picked the commits of #628 so the author is remembered.

Basically this patch renders the `RadioField` `render_kw` parameter in both the generated `<ul>` tag, and the `<input type="radio">` tags inside. Before it was rendered only in `<ul>` and not in the `<input>`.

Passing `disabled=True` in the `render_kw` will keep adding a `disabled` parameter in `<ul>`. This may look wrong but this is harmless and not worse than before.

I am not sure how to improve this. For instance, how to ventilate `render_kw` items so some are displayed in `<ul>` and other in `<input>`? What about `class`, should this be rendered in both?

I think the situation is still unsatisfying with this patch, but at least it goes in the right direction. It is a *good-enough* PR in my opinion.

What do you think?


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 8bb26bc..c846991 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -9,6 +9,8 @@ Unreleased
 -   :meth:`~fields.FormField.populate_obj` always calls :func:`setattr`
     :pr:`675`
 -   WTForms has a new logo. :issue:`569` :pr:`689`
+-   Fixed :class:`~fields.RadioField` `render_kw` rendering. :issue:`490`
+    :pr:`628` :pr:`688`
 
 Version 3.0.0a1
 ---------------
diff --git a/src/wtforms/fields/core.py b/src/wtforms/fields/core.py
index c5045f3..96484a9 100644
--- a/src/wtforms/fields/core.py
+++ b/src/wtforms/fields/core.py
@@ -503,6 +503,7 @@ class SelectFieldBase(Field):
             widget=self.option_widget,
             validators=self.validators,
             name=self.name,
+            render_kw=self.render_kw,
             _form=None,
             _meta=self.meta,
         )


Test patch:
diff --git a/tests/fields/test_radio.py b/tests/fields/test_radio.py
index 863b933..e817972 100644
--- a/tests/fields/test_radio.py
+++ b/tests/fields/test_radio.py
@@ -102,3 +102,19 @@ def test_required_validator():
     form = F(DummyPostData(b=1))
     assert form.validate() is False
     assert form.c.errors == ["This field is required."]
+
+
+def test_render_kw_preserved():
+    F = make_form(
+        a=RadioField(
+            choices=[(True, "yes"), (False, "no")], render_kw=dict(disabled=True)
+        )
+    )
+    form = F()
+    assert form.a() == (
+        '<ul disabled id="a">'
+        '<li><input disabled id="a-0" name="a" type="radio" value="True"> '
+        '<label for="a-0">yes</label></li><li>'
+        '<input disabled id="a-1" name="a" type="radio" value="False"> '
+        '<label for="a-1">no</label></li></ul>'
+    )
diff --git a/tests/fields/test_select.py b/tests/fields/test_select.py
index bbfbef4..c411f4a 100644
--- a/tests/fields/test_select.py
+++ b/tests/fields/test_select.py
@@ -195,3 +195,16 @@ def test_required_validator():
     form = F()
     assert form.validate() is False
     assert form.c.errors == ["This field is required."]
+
+
+def test_render_kw_preserved():
+    F = make_form(
+        a=SelectField(choices=[("foo"), ("bar")], render_kw=dict(disabled=True))
+    )
+    form = F()
+    assert form.a() == (
+        '<select disabled id="a" name="a">'
+        '<option value="foo">foo</option>'
+        '<option value="bar">bar</option>'
+        "</select>"
+    )
diff --git a/tests/fields/test_selectmultiple.py b/tests/fields/test_selectmultiple.py
index 102d93e..14328e2 100644
--- a/tests/fields/test_selectmultiple.py
+++ b/tests/fields/test_selectmultiple.py
@@ -136,3 +136,16 @@ def test_required_validator():
     form = F()
     assert form.validate() is False
     assert form.c.errors == ["This field is required."]
+
+
+def test_render_kw_preserved():
+    F = make_form(
+        a=SelectMultipleField(choices=[("foo"), ("bar")], render_kw=dict(disabled=True))
+    )
+    form = F()
+    assert form.a() == (
+        '<select disabled id="a" multiple name="a">'
+        '<option value="foo">foo</option>'
+        '<option value="bar">bar</option>'
+        "</select>"
+    )