Fixed #4228 -- Removed hardcoding of `RadioFieldRenderer` in the `RadioSelect` Widget so that the display of `RadioSelect`s can be more easily customized. `BoundField.__unicode__` also no longer special cases `RadioSelect` since `RadioSelect.render()` now returns a string like every other Widget.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5782 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Gary Wilson Jr 2007-08-01 05:41:32 +00:00
parent f27774ee0a
commit cdbd5751d3
3 changed files with 52 additions and 20 deletions

View File

@ -232,16 +232,8 @@ class BoundField(StrAndUnicode):
self.help_text = field.help_text or '' self.help_text = field.help_text or ''
def __unicode__(self): def __unicode__(self):
"Renders this field as an HTML widget." """Renders this field as an HTML widget."""
# Use the 'widget' attribute on the field to determine which type return self.as_widget()
# of HTML widget to use.
value = self.as_widget(self.field.widget)
if not isinstance(value, basestring):
# Some Widget render() methods -- notably RadioSelect -- return a
# "special" object rather than a string. Call __unicode__() on that
# object to get its rendered value.
value = unicode(value)
return value
def _errors(self): def _errors(self):
""" """
@ -251,7 +243,14 @@ class BoundField(StrAndUnicode):
return self.form.errors.get(self.name, ErrorList()) return self.form.errors.get(self.name, ErrorList())
errors = property(_errors) errors = property(_errors)
def as_widget(self, widget, attrs=None): def as_widget(self, widget=None, attrs=None):
"""
Renders the field by rendering the passed widget, adding any HTML
attributes passed as attrs. If no widget is specified, then the
field's default widget will be used.
"""
if not widget:
widget = self.field.widget
attrs = attrs or {} attrs = attrs or {}
auto_id = self.auto_id auto_id = self.auto_id
if auto_id and 'id' not in attrs and 'id' not in widget.attrs: if auto_id and 'id' not in attrs and 'id' not in widget.attrs:

View File

@ -216,7 +216,11 @@ class SelectMultiple(Widget):
return data.get(name, None) return data.get(name, None)
class RadioInput(StrAndUnicode): class RadioInput(StrAndUnicode):
"An object used by RadioFieldRenderer that represents a single <input type='radio'>." """
An object used by RadioFieldRenderer that represents a single
<input type='radio'>.
"""
def __init__(self, name, value, attrs, choice, index): def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value self.name, self.value = name, value
self.attrs = attrs self.attrs = attrs
@ -239,7 +243,10 @@ class RadioInput(StrAndUnicode):
return u'<input%s />' % flatatt(final_attrs) return u'<input%s />' % flatatt(final_attrs)
class RadioFieldRenderer(StrAndUnicode): class RadioFieldRenderer(StrAndUnicode):
"An object used by RadioSelect to enable customization of radio widgets." """
An object used by RadioSelect to enable customization of radio widgets.
"""
def __init__(self, name, value, attrs, choices): def __init__(self, name, value, attrs, choices):
self.name, self.value, self.attrs = name, value, attrs self.name, self.value, self.attrs = name, value, attrs
self.choices = choices self.choices = choices
@ -253,16 +260,30 @@ class RadioFieldRenderer(StrAndUnicode):
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx) return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
def __unicode__(self): def __unicode__(self):
"Outputs a <ul> for this set of radio fields." return self.render()
def render(self):
"""Outputs a <ul> for this set of radio fields."""
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]) return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self])
class RadioSelect(Select): class RadioSelect(Select):
def render(self, name, value, attrs=None, choices=()):
"Returns a RadioFieldRenderer instance rather than a Unicode string." def __init__(self, *args, **kwargs):
self.renderer = kwargs.pop('renderer', None)
if not self.renderer:
self.renderer = RadioFieldRenderer
super(RadioSelect, self).__init__(*args, **kwargs)
def get_renderer(self, name, value, attrs=None, choices=()):
"""Returns an instance of the renderer."""
if value is None: value = '' if value is None: value = ''
str_value = force_unicode(value) # Normalize to string. str_value = force_unicode(value) # Normalize to string.
final_attrs = self.build_attrs(attrs) final_attrs = self.build_attrs(attrs)
return RadioFieldRenderer(name, str_value, final_attrs, list(chain(self.choices, choices))) choices = list(chain(self.choices, choices))
return self.renderer(name, str_value, final_attrs, choices)
def render(self, name, value, attrs=None, choices=()):
return self.get_renderer(name, value, attrs, choices).render()
def id_for_label(self, id_): def id_for_label(self, id_):
# RadioSelect is represented by multiple <input type="radio"> fields, # RadioSelect is represented by multiple <input type="radio"> fields,

View File

@ -4,6 +4,7 @@ from regressions import regression_tests
form_tests = r""" form_tests = r"""
>>> from django.newforms import * >>> from django.newforms import *
>>> from django.newforms.widgets import RadioFieldRenderer
>>> import datetime >>> import datetime
>>> import time >>> import time
>>> import re >>> import re
@ -614,11 +615,11 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
<li><label><input type="radio" name="num" value="5" /> 5</label></li> <li><label><input type="radio" name="num" value="5" /> 5</label></li>
</ul> </ul>
The render() method returns a RadioFieldRenderer object, whose str() is a <ul>. RadioSelect uses a RadioFieldRenderer to render the individual radio inputs.
You can manipulate that object directly to customize the way the RadioSelect You can manipulate that object directly to customize the way the RadioSelect
is rendered. is rendered.
>>> w = RadioSelect() >>> w = RadioSelect()
>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) >>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> for inp in r: >>> for inp in r:
... print inp ... print inp
<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label> <label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
@ -644,10 +645,21 @@ beatle J P Paul False
beatle J G George False beatle J G George False
beatle J R Ringo False beatle J R Ringo False
You can create your own custom renderers for RadioSelect to use.
>>> class MyRenderer(RadioFieldRenderer):
... def render(self):
... return u'<br />\n'.join([unicode(choice) for choice in self])
>>> w = RadioSelect(renderer=MyRenderer)
>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
<label><input type="radio" name="beatle" value="J" /> John</label><br />
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
<label><input type="radio" name="beatle" value="R" /> Ringo</label>
A RadioFieldRenderer object also allows index access to individual RadioInput A RadioFieldRenderer object also allows index access to individual RadioInput
objects. objects.
>>> w = RadioSelect() >>> w = RadioSelect()
>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))) >>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> print r[1] >>> print r[1]
<label><input type="radio" name="beatle" value="P" /> Paul</label> <label><input type="radio" name="beatle" value="P" /> Paul</label>
>>> print r[0] >>> print r[0]