newforms: The <input> tags in a RadioSelect now each have a distinct ID. Also, this plays nicely with auto_id and <label>s for Form.as_table() and Form.as_ul(). Refs #3064
git-svn-id: http://code.djangoproject.com/svn/django/trunk@4131 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
bb45c394a6
commit
fe4af48ec8
|
@ -202,9 +202,10 @@ class BoundField(object):
|
||||||
field's HTML-escaped verbose_name.
|
field's HTML-escaped verbose_name.
|
||||||
"""
|
"""
|
||||||
contents = contents or escape(self.verbose_name)
|
contents = contents or escape(self.verbose_name)
|
||||||
id_ = self._field.widget.attrs.get('id') or self.auto_id
|
widget = self._field.widget
|
||||||
|
id_ = widget.attrs.get('id') or self.auto_id
|
||||||
if id_:
|
if id_:
|
||||||
contents = '<label for="%s">%s</label>' % (id_, contents)
|
contents = '<label for="%s">%s</label>' % (widget.id_for_label(id_), contents)
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
def _auto_id(self):
|
def _auto_id(self):
|
||||||
|
|
|
@ -35,6 +35,19 @@ class Widget(object):
|
||||||
attrs.update(extra_attrs)
|
attrs.update(extra_attrs)
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def id_for_label(self, id_):
|
||||||
|
"""
|
||||||
|
Returns the HTML ID attribute of this Widget for use by a <label>,
|
||||||
|
given the ID of the field. Returns None if no ID is available.
|
||||||
|
|
||||||
|
This hook is necessary because some widgets have multiple HTML
|
||||||
|
elements and, thus, multiple IDs. In that case, this method should
|
||||||
|
return an ID value that corresponds to the first ID in the widget's
|
||||||
|
tags.
|
||||||
|
"""
|
||||||
|
return id_
|
||||||
|
id_for_label = classmethod(id_for_label)
|
||||||
|
|
||||||
class Input(Widget):
|
class Input(Widget):
|
||||||
"""
|
"""
|
||||||
Base class for all <input> widgets (except type='checkbox' and
|
Base class for all <input> widgets (except type='checkbox' and
|
||||||
|
@ -111,10 +124,11 @@ class SelectMultiple(Widget):
|
||||||
|
|
||||||
class RadioInput(object):
|
class RadioInput(object):
|
||||||
"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):
|
def __init__(self, name, value, attrs, choice, index):
|
||||||
self.name, self.value = name, value
|
self.name, self.value = name, value
|
||||||
self.attrs = attrs or {}
|
self.attrs = attrs
|
||||||
self.choice_value, self.choice_label = choice
|
self.choice_value, self.choice_label = choice
|
||||||
|
self.index = index
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
return u'<label>%s %s</label>' % (self.tag(), self.choice_label)
|
||||||
|
@ -123,6 +137,8 @@ class RadioInput(object):
|
||||||
return self.value == smart_unicode(self.choice_value)
|
return self.value == smart_unicode(self.choice_value)
|
||||||
|
|
||||||
def tag(self):
|
def tag(self):
|
||||||
|
if self.attrs.has_key('id'):
|
||||||
|
self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index)
|
||||||
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
|
final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value)
|
||||||
if self.is_checked():
|
if self.is_checked():
|
||||||
final_attrs['checked'] = 'checked'
|
final_attrs['checked'] = 'checked'
|
||||||
|
@ -135,8 +151,8 @@ class RadioFieldRenderer(object):
|
||||||
self.choices = choices
|
self.choices = choices
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for choice in self.choices:
|
for i, choice in enumerate(self.choices):
|
||||||
yield RadioInput(self.name, self.value, self.attrs, choice)
|
yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"Outputs a <ul> for this set of radio fields."
|
"Outputs a <ul> for this set of radio fields."
|
||||||
|
@ -147,7 +163,18 @@ class RadioSelect(Select):
|
||||||
"Returns a RadioFieldRenderer instance rather than a Unicode string."
|
"Returns a RadioFieldRenderer instance rather than a Unicode string."
|
||||||
if value is None: value = ''
|
if value is None: value = ''
|
||||||
str_value = smart_unicode(value) # Normalize to string.
|
str_value = smart_unicode(value) # Normalize to string.
|
||||||
|
attrs = attrs or {}
|
||||||
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
|
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
|
||||||
|
|
||||||
|
def id_for_label(self, id_):
|
||||||
|
# RadioSelect is represented by multiple <input type="radio"> fields,
|
||||||
|
# each of which has a distinct ID. The IDs are made distinct by a "_X"
|
||||||
|
# suffix, where X is the zero-based index of the radio field. Thus,
|
||||||
|
# the label for a RadioSelect should reference the first one ('_0').
|
||||||
|
if id_:
|
||||||
|
id_ += '_0'
|
||||||
|
return id_
|
||||||
|
id_for_label = classmethod(id_for_label)
|
||||||
|
|
||||||
class CheckboxSelectMultiple(Widget):
|
class CheckboxSelectMultiple(Widget):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1373,6 +1373,44 @@ For a form with a <select>, use ChoiceField:
|
||||||
<li><label><input type="radio" name="language" value="P" /> Python</label></li>
|
<li><label><input type="radio" name="language" value="P" /> Python</label></li>
|
||||||
<li><label><input type="radio" name="language" value="J" /> Java</label></li>
|
<li><label><input type="radio" name="language" value="J" /> Java</label></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
>>> print f
|
||||||
|
<tr><td>Name:</td><td><input type="text" name="name" /></td></tr>
|
||||||
|
<tr><td>Language:</td><td><ul>
|
||||||
|
<li><label><input type="radio" name="language" value="P" /> Python</label></li>
|
||||||
|
<li><label><input type="radio" name="language" value="J" /> Java</label></li>
|
||||||
|
</ul></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li>Name: <input type="text" name="name" /></li>
|
||||||
|
<li>Language: <ul>
|
||||||
|
<li><label><input type="radio" name="language" value="P" /> Python</label></li>
|
||||||
|
<li><label><input type="radio" name="language" value="J" /> Java</label></li>
|
||||||
|
</ul></li>
|
||||||
|
|
||||||
|
Regarding auto_id and <label>, RadioSelect is a special case. Each radio button
|
||||||
|
gets a distinct ID, formed by appending an underscore plus the button's
|
||||||
|
zero-based index.
|
||||||
|
>>> f = FrameworkForm(auto_id='id_%s')
|
||||||
|
>>> print f['language']
|
||||||
|
<ul>
|
||||||
|
<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li>
|
||||||
|
<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
When RadioSelect is used with auto_id, and the whole form is printed using
|
||||||
|
either as_table() or as_ul(), the label for the RadioSelect will point to the
|
||||||
|
ID of the *first* radio button.
|
||||||
|
>>> print f
|
||||||
|
<tr><td><label for="id_name">Name:</label></td><td><input type="text" name="name" id="id_name" /></td></tr>
|
||||||
|
<tr><td><label for="id_language_0">Language:</label></td><td><ul>
|
||||||
|
<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li>
|
||||||
|
<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
|
||||||
|
</ul></td></tr>
|
||||||
|
>>> print f.as_ul()
|
||||||
|
<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li>
|
||||||
|
<li><label for="id_language_0">Language:</label> <ul>
|
||||||
|
<li><label><input type="radio" id="id_language_0" value="P" name="language" /> Python</label></li>
|
||||||
|
<li><label><input type="radio" id="id_language_1" value="J" name="language" /> Java</label></li>
|
||||||
|
</ul></li>
|
||||||
|
|
||||||
MultipleChoiceField is a special case, as its data is required to be a list:
|
MultipleChoiceField is a special case, as its data is required to be a list:
|
||||||
>>> class SongForm(Form):
|
>>> class SongForm(Form):
|
||||||
|
|
Loading…
Reference in New Issue