diff --git a/django/newforms/widgets.py b/django/newforms/widgets.py
index b24f91f4c6..c9b3d02913 100644
--- a/django/newforms/widgets.py
+++ b/django/newforms/widgets.py
@@ -5,7 +5,7 @@ HTML Widget classes
__all__ = (
'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput',
'Textarea', 'CheckboxInput',
- 'Select', 'SelectMultiple',
+ 'Select', 'SelectMultiple', 'RadioSelect',
)
from django.utils.html import escape
@@ -35,7 +35,10 @@ class Widget(object):
return attrs
class Input(Widget):
- "Base class for all widgets (except type='checkbox', which is special)"
+ """
+ Base class for all widgets (except type='checkbox' and
+ type='radio', which are special).
+ """
input_type = None # Subclasses must define this.
def render(self, name, value, attrs=None):
if value is None: value = ''
@@ -102,8 +105,45 @@ class SelectMultiple(Widget):
output.append(u'')
return u'\n'.join(output)
-class RadioSelect(Widget):
- pass
+class RadioInput(object):
+ "An object used by RadioFieldRenderer that represents a single ."
+ def __init__(self, name, value, attrs, choice):
+ self.name, self.value = name, value
+ self.attrs = attrs or {}
+ self.choice_value, self.choice_label = choice
+
+ def __str__(self):
+ return u'' % (self.tag(), self.choice_label)
+
+ def is_checked(self):
+ return self.value == str(self.choice_value)
+
+ def tag(self):
+ final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
+ if self.is_checked():
+ final_attrs['checked'] = 'checked'
+ return u'' % flatatt(final_attrs)
+
+class RadioFieldRenderer(object):
+ "An object used by RadioSelect to enable customization of radio widgets."
+ def __init__(self, name, value, attrs, choices):
+ self.name, self.value, self.attrs = name, value, attrs
+ self.choices = choices
+
+ def __iter__(self):
+ for choice in self.choices:
+ yield RadioInput(self.name, self.value, self.attrs, choice)
+
+ def __str__(self):
+ "Outputs a
for this set of radio fields."
+ return u'
\n%s\n
' % u'\n'.join([u'
%s
' % w for w in self])
+
+class RadioSelect(Select):
+ def render(self, name, value, attrs=None, choices=()):
+ "Returns a RadioFieldRenderer instance rather than a Unicode string."
+ if value is None: value = ''
+ str_value = str(value) # Normalize to string.
+ return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
class CheckboxSelectMultiple(Widget):
pass
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 5402f654e9..42596b02e0 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -340,6 +340,117 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
+# RadioSelect Widget ##########################################################
+
+>>> w = RadioSelect()
+>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+
+
+
+
+
+
+
+If the value is None, none of the options are checked:
+>>> print w.render('beatle', None, choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+
+
+
+
+
+
+
+If the value corresponds to a label (but not to an option value), none of the options are checked:
+>>> print w.render('beatle', 'John', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+
+
+
+
+
+
+
+The value is compared to its str():
+>>> print w.render('num', 2, choices=[('1', '1'), ('2', '2'), ('3', '3')])
+
+
+The 'choices' argument can be any iterable:
+>>> def get_choices():
+... for i in range(5):
+... yield (i, i)
+>>> print w.render('num', 2, choices=get_choices())
+
+
+
+
+
+
+
+
+You can also pass 'choices' to the constructor:
+>>> w = RadioSelect(choices=[(1, 1), (2, 2), (3, 3)])
+>>> print w.render('num', 2)
+
+
+
+
+
+
+If 'choices' is passed to both the constructor and render(), then they'll both be in the output:
+>>> print w.render('num', 2, choices=[(4, 4), (5, 5)])
+
+
+
+
+
+
+
+
+The render() method returns a RadioFieldRenderer object, whose str() is a
.
+You can manipulate that object directly to customize the way the RadioSelect
+is rendered.
+>>> w = RadioSelect()
+>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+>>> for inp in r:
+... print inp
+
+
+
+
+>>> for inp in r:
+... print '%s ' % inp
+
+
+
+
+>>> for inp in r:
+... print '
%s %s
' % (inp.tag(), inp.choice_label)
+
John
+
Paul
+
George
+
Ringo
+>>> for inp in r:
+... print '%s %s %s %s %s' % (inp.name, inp.value, inp.choice_value, inp.choice_label, inp.is_checked())
+beatle J J John True
+beatle J P Paul False
+beatle J G George False
+beatle J R Ringo False
+
# CharField ###################################################################
>>> f = CharField(required=False)