Fixed #3064 -- newforms: Added <label> support through BoundField.label_tag() method. Also added BoundField.verbose_name and added/updated unit tests. Thanks, SmileyChris

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4130 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2006-11-29 00:49:27 +00:00
parent 3d89f26b08
commit bb45c394a6
2 changed files with 81 additions and 16 deletions

View File

@ -3,6 +3,7 @@ Form classes
""" """
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.html import escape
from fields import Field from fields import Field
from widgets import TextInput, Textarea from widgets import TextInput, Textarea
from util import ErrorDict, ErrorList, ValidationError from util import ErrorDict, ErrorList, ValidationError
@ -81,7 +82,7 @@ class Form(object):
bf = BoundField(self, field, name) bf = BoundField(self, field, name)
if bf.errors: if bf.errors:
output.append(u'<tr><td colspan="2">%s</td></tr>' % bf.errors) output.append(u'<tr><td colspan="2">%s</td></tr>' % bf.errors)
output.append(u'<tr><td>%s:</td><td>%s</td></tr>' % (bf.label, bf)) output.append(u'<tr><td>%s</td><td>%s</td></tr>' % (bf.label_tag(bf.verbose_name+':'), bf))
return u'\n'.join(output) return u'\n'.join(output)
def as_ul(self): def as_ul(self):
@ -95,7 +96,7 @@ class Form(object):
line = u'<li>' line = u'<li>'
if bf.errors: if bf.errors:
line += str(bf.errors) line += str(bf.errors)
line += u'%s: %s</li>' % (bf.label, bf) line += u'%s %s</li>' % (bf.label_tag(bf.verbose_name+':'), bf)
output.append(line) output.append(line)
return u'\n'.join(output) return u'\n'.join(output)
@ -190,9 +191,21 @@ class BoundField(object):
"Returns a string of HTML for representing this as a <textarea>." "Returns a string of HTML for representing this as a <textarea>."
return self.as_widget(Textarea(), attrs) return self.as_widget(Textarea(), attrs)
def _label(self): def _verbose_name(self):
return pretty_name(self._name) return pretty_name(self._name)
label = property(_label) verbose_name = property(_verbose_name)
def label_tag(self, contents=None):
"""
Wraps the given contents in a <label>, if the field has an ID attribute.
Does not HTML-escape the contents. If contents aren't given, uses the
field's HTML-escaped verbose_name.
"""
contents = contents or escape(self.verbose_name)
id_ = self._field.widget.attrs.get('id') or self.auto_id
if id_:
contents = '<label for="%s">%s</label>' % (id_, contents)
return contents
def _auto_id(self): def _auto_id(self):
""" """

View File

@ -1251,20 +1251,25 @@ u'* This field is required.'
"auto_id" tells the Form to add an "id" attribute to each form element. "auto_id" tells the Form to add an "id" attribute to each form element.
If it's a string that contains '%s', Django will use that as a format string If it's a string that contains '%s', Django will use that as a format string
into which the field's name will be inserted. into which the field's name will be inserted. It will also put a <label> around
the human-readable labels for a field.
>>> p = Person(auto_id='id_%s') >>> p = Person(auto_id='id_%s')
>>> print p.as_ul() >>> print p.as_ul()
<li>First name: <input type="text" name="first_name" id="id_first_name" /></li> <li><label for="id_first_name">First name:</label> <input type="text" name="first_name" id="id_first_name" /></li>
<li>Last name: <input type="text" name="last_name" id="id_last_name" /></li> <li><label for="id_last_name">Last name:</label> <input type="text" name="last_name" id="id_last_name" /></li>
<li>Birthday: <input type="text" name="birthday" id="id_birthday" /></li> <li><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" id="id_birthday" /></li>
>>> print p.as_table()
<tr><td><label for="id_first_name">First name:</label></td><td><input type="text" name="first_name" id="id_first_name" /></td></tr>
<tr><td><label for="id_last_name">Last name:</label></td><td><input type="text" name="last_name" id="id_last_name" /></td></tr>
<tr><td><label for="id_birthday">Birthday:</label></td><td><input type="text" name="birthday" id="id_birthday" /></td></tr>
If auto_id is any True value whose str() does not contain '%s', the "id" If auto_id is any True value whose str() does not contain '%s', the "id"
attribute will be the name of the field. attribute will be the name of the field.
>>> p = Person(auto_id=True) >>> p = Person(auto_id=True)
>>> print p.as_ul() >>> print p.as_ul()
<li>First name: <input type="text" name="first_name" id="first_name" /></li> <li><label for="first_name">First name:</label> <input type="text" name="first_name" id="first_name" /></li>
<li>Last name: <input type="text" name="last_name" id="last_name" /></li> <li><label for="last_name">Last name:</label> <input type="text" name="last_name" id="last_name" /></li>
<li>Birthday: <input type="text" name="birthday" id="birthday" /></li> <li><label for="birthday">Birthday:</label> <input type="text" name="birthday" id="birthday" /></li>
If auto_id is any False value, an "id" attribute won't be output unless it If auto_id is any False value, an "id" attribute won't be output unless it
was manually entered. was manually entered.
@ -1275,14 +1280,14 @@ was manually entered.
<li>Birthday: <input type="text" name="birthday" /></li> <li>Birthday: <input type="text" name="birthday" /></li>
In this example, auto_id is False, but the "id" attribute for the "first_name" In this example, auto_id is False, but the "id" attribute for the "first_name"
field is given. field is given. Also note that field gets a <label>, while the others don't.
>>> class PersonNew(Form): >>> class PersonNew(Form):
... first_name = CharField(widget=TextInput(attrs={'id': 'first_name_id'})) ... first_name = CharField(widget=TextInput(attrs={'id': 'first_name_id'}))
... last_name = CharField() ... last_name = CharField()
... birthday = DateField() ... birthday = DateField()
>>> p = PersonNew(auto_id=False) >>> p = PersonNew(auto_id=False)
>>> print p.as_ul() >>> print p.as_ul()
<li>First name: <input type="text" id="first_name_id" name="first_name" /></li> <li><label for="first_name_id">First name:</label> <input type="text" id="first_name_id" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" /></li> <li>Last name: <input type="text" name="last_name" /></li>
<li>Birthday: <input type="text" name="birthday" /></li> <li>Birthday: <input type="text" name="birthday" /></li>
@ -1290,9 +1295,9 @@ If the "id" attribute is specified in the Form and auto_id is True, the "id"
attribute in the Form gets precedence. attribute in the Form gets precedence.
>>> p = PersonNew(auto_id=True) >>> p = PersonNew(auto_id=True)
>>> print p.as_ul() >>> print p.as_ul()
<li>First name: <input type="text" id="first_name_id" name="first_name" /></li> <li><label for="first_name_id">First name:</label> <input type="text" id="first_name_id" name="first_name" /></li>
<li>Last name: <input type="text" name="last_name" id="last_name" /></li> <li><label for="last_name">Last name:</label> <input type="text" name="last_name" id="last_name" /></li>
<li>Birthday: <input type="text" name="birthday" id="birthday" /></li> <li><label for="birthday">Birthday:</label> <input type="text" name="birthday" id="birthday" /></li>
>>> class SignupForm(Form): >>> class SignupForm(Form):
... email = EmailField() ... email = EmailField()
@ -1606,10 +1611,57 @@ particular field.
<input type="submit" /> <input type="submit" />
</form> </form>
Use form.[field].verbose_name to output a field's "verbose name" -- its field
name with underscores converted to spaces, and the initial letter capitalized.
>>> t = Template('''<form action="">
... <p><label>{{ form.username.verbose_name }}: {{ form.username }}</label></p>
... <p><label>{{ form.password1.verbose_name }}: {{ form.password1 }}</label></p>
... <p><label>{{ form.password2.verbose_name }}: {{ form.password2 }}</label></p>
... <input type="submit" />
... </form>''')
>>> print t.render(Context({'form': UserRegistration()}))
<form action="">
<p><label>Username: <input type="text" name="username" /></label></p>
<p><label>Password1: <input type="password" name="password1" /></label></p>
<p><label>Password2: <input type="password" name="password2" /></label></p>
<input type="submit" />
</form>
User form.[field].label_tag to output a field's verbose_name with a <label>
tag wrapped around it, but *only* if the given field has an "id" attribute.
Recall from above that passing the "auto_id" argument to a Form gives each
field an "id" attribute.
>>> t = Template('''<form action="">
... <p>{{ form.username.label_tag }}: {{ form.username }}</p>
... <p>{{ form.password1.label_tag }}: {{ form.password1 }}</p>
... <p>{{ form.password2.label_tag }}: {{ form.password2 }}</p>
... <input type="submit" />
... </form>''')
>>> print t.render(Context({'form': UserRegistration()}))
<form action="">
<p>Username: <input type="text" name="username" /></p>
<p>Password1: <input type="password" name="password1" /></p>
<p>Password2: <input type="password" name="password2" /></p>
<input type="submit" />
</form>
>>> print t.render(Context({'form': UserRegistration(auto_id='id_%s')}))
<form action="">
<p><label for="id_username">Username</label>: <input type="text" name="username" id="id_username" /></p>
<p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p>
<p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p>
<input type="submit" />
</form>
To display the errors that aren't associated with a particular field -- e.g., To display the errors that aren't associated with a particular field -- e.g.,
the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the the errors caused by Form.clean() -- use {{ form.non_field_errors }} in the
template. If used on its own, it is displayed as a <ul> (or an empty string, if template. If used on its own, it is displayed as a <ul> (or an empty string, if
the list of errors is empty). You can also use it in {% if %} statements. the list of errors is empty). You can also use it in {% if %} statements.
>>> t = Template('''<form action="">
... {{ form.username.errors.as_ul }}<p><label>Your username: {{ form.username }}</label></p>
... {{ form.password1.errors.as_ul }}<p><label>Password: {{ form.password1 }}</label></p>
... {{ form.password2.errors.as_ul }}<p><label>Password (again): {{ form.password2 }}</label></p>
... <input type="submit" />
... </form>''')
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})})) >>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'})}))
<form action=""> <form action="">
<p><label>Your username: <input type="text" name="username" value="django" /></label></p> <p><label>Your username: <input type="text" name="username" value="django" /></label></p>