Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #18134 -- BoundField.label_tag now includes the form's label_su…

…ffix

There was an inconsistency between how the label_tag for forms were
generated depending on which method was used: as_p, as_ul and as_table
contained code to append the label_suffix where as label_tag called on a
form field directly did NOT append the label_suffix. The code for
appending the label_suffix has been moved in to the label_tag code of
the field and the HTML generation code for as_p, as_ul and as_table now
calls this code as well.

This is a backwards incompatible change because users who have added the
label_suffix manually in their templates may now get double label_suffix
characters in their forms.
  • Loading branch information...
commit 584bd14dcfdee9585fec7794d53ce120ea73d0bc 1 parent a643e4d
@gabejackson gabejackson authored timgraham committed
View
9 django/forms/forms.py
@@ -170,11 +170,6 @@ def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_
if bf.label:
label = conditional_escape(force_text(bf.label))
- # Only add the suffix if the label does not end in
- # punctuation.
- if self.label_suffix:
- if label[-1] not in ':?.!':
- label = format_html('{0}{1}', label, self.label_suffix)
label = bf.label_tag(label) or ''
else:
label = ''
@@ -522,6 +517,10 @@ def label_tag(self, contents=None, attrs=None):
If attrs are given, they're used as HTML attributes on the <label> tag.
"""
contents = contents or self.label
+ # Only add the suffix if the label does not end in punctuation.
+ if self.form.label_suffix:
+ if contents[-1] not in ':?.!':
+ contents = format_html('{0}{1}', contents, self.form.label_suffix)
widget = self.field.widget
id_ = widget.attrs.get('id') or self.auto_id
if id_:
View
9 docs/ref/forms/api.txt
@@ -498,6 +498,8 @@ include ``%s`` -- then the library will act as if ``auto_id`` is ``True``.
By default, ``auto_id`` is set to the string ``'id_%s'``.
+.. attribute:: Form.label_suffix
+
Normally, a colon (``:``) will be appended after any label name when a form is
rendered. It's possible to change the colon to another character, or omit it
entirely, using the ``label_suffix`` parameter::
@@ -650,12 +652,17 @@ To separately render the label tag of a form field, you can call its
>>> f = ContactForm(data)
>>> print(f['message'].label_tag())
- <label for="id_message">Message</label>
+ <label for="id_message">Message:</label>
Optionally, you can provide the ``contents`` parameter which will replace the
auto-generated label tag. An optional ``attrs`` dictionary may contain
additional attributes for the ``<label>`` tag.
+.. versionchanged:: 1.6
+
+ The label now includes the form's :attr:`~django.forms.Form.label_suffix`
+ (a semicolon, by default).
+
.. method:: BoundField.css_classes()
When you use Django's rendering shortcuts, CSS classes are used to
View
31 docs/releases/1.6.txt
@@ -581,6 +581,37 @@ It is still possible to convert the fetched rows to ``Model`` objects
lazily by using the :meth:`~django.db.models.query.QuerySet.iterator()`
method.
+:meth:`BoundField.label_tag<django.forms.BoundField.label_tag>` now includes the form's :attr:`~django.forms.Form.label_suffix`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is consistent with how methods like
+:meth:`Form.as_p<django.forms.Form.as_p>` and
+:meth:`Form.as_ul<django.forms.Form.as_ul>` render labels.
+
+If you manually render ``label_tag`` in your templates:
+
+.. code-block:: html+django
+
+ {{ form.my_field.label_tag }}: {{ form.my_field }}
+
+you'll want to remove the semicolon (or whatever other separator you may be
+using) to avoid duplicating it when upgrading to Django 1.6. The following
+template in Django 1.6 will render identically to the above template in Django
+1.5, except that the semicolon will appear inside the ``<label>`` element.
+
+.. code-block:: html+django
+
+ {{ form.my_field.label_tag }} {{ form.my_field }}
+
+will render something like:
+
+.. code-block:: html
+
+ <label for="id_my_field">My Field:</label> <input id="id_my_field" type="text" name="my_field" />
+
+If you want to keep the current behavior of rendering ``label_tag`` without
+the ``label_suffix``, instantiate the form ``label_suffix=''``.
+
Miscellaneous
~~~~~~~~~~~~~
View
16 docs/topics/forms/index.txt
@@ -302,7 +302,7 @@ loop::
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
- {{ field.label_tag }}: {{ field }}
+ {{ field.label_tag }} {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
@@ -316,8 +316,14 @@ attributes, which can be useful in your templates:
The label of the field, e.g. ``Email address``.
``{{ field.label_tag }}``
- The field's label wrapped in the appropriate HTML ``<label>`` tag,
- e.g. ``<label for="id_email">Email address</label>``
+ The field's label wrapped in the appropriate HTML ``<label>`` tag.
+
+ .. versionchanged:: 1.6
+
+ This includes the form's :attr:`~django.forms.Form.label_suffix`. For
+ example, the default ``label_suffix`` is a semicolon::
+
+ <label for="id_email">Email address:</label>
``{{ field.value }}``
The value of the field. e.g ``someone@example.com``
@@ -375,7 +381,7 @@ these two methods::
{% for field in form.visible_fields %}
<div class="fieldWrapper">
{{ field.errors }}
- {{ field.label_tag }}: {{ field }}
+ {{ field.label_tag }} {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
@@ -403,7 +409,7 @@ using the :ttag:`include` tag to reuse it in other templates::
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
- {{ field.label_tag }}: {{ field }}
+ {{ field.label_tag }} {{ field }}
</div>
{% endfor %}
View
2  docs/topics/forms/modelforms.txt
@@ -907,7 +907,7 @@ Third, you can manually render each field::
{{ formset.management_form }}
{% for form in formset %}
{% for field in form %}
- {{ field.label_tag }}: {{ field }}
+ {{ field.label_tag }} {{ field }}
{% endfor %}
{% endfor %}
</form>
View
4 tests/admin_util/tests.py
@@ -301,7 +301,7 @@ class MyForm(forms.Form):
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
'<label for="id_text" class="required inline"><i>text</i>:</label>')
self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
- '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i></label>')
+ '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i>:</label>')
# normal strings needs to be escaped
class MyForm(forms.Form):
@@ -312,7 +312,7 @@ class MyForm(forms.Form):
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
'<label for="id_text" class="required inline">&amp;text:</label>')
self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
- '<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb</label>')
+ '<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb:</label>')
def test_flatten_fieldsets(self):
"""
View
36 tests/forms_tests/tests/test_forms.py
@@ -1589,9 +1589,9 @@ def clean(self):
# 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>
+<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>''')
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
@@ -1601,18 +1601,18 @@ def clean(self):
<input type="submit" />
</form>""")
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id='id_%s')})), """<form action="">
-<p><label for="id_username">Username</label>: <input id="id_username" type="text" name="username" maxlength="10" /></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>
+<p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></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>""")
# User form.[field].help_text to output a field's help text. If the given field
# does not have help text, nothing will be output.
t = Template('''<form action="">
-<p>{{ form.username.label_tag }}: {{ form.username }}<br />{{ form.username.help_text }}</p>
-<p>{{ form.password1.label_tag }}: {{ form.password1 }}</p>
-<p>{{ form.password2.label_tag }}: {{ form.password2 }}</p>
+<p>{{ form.username.label_tag }} {{ form.username }}<br />{{ form.username.help_text }}</p>
+<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>
+<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>
<input type="submit" />
</form>''')
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
@@ -1819,17 +1819,17 @@ class SomeForm(Form):
testcases = [ # (args, kwargs, expected)
# without anything: just print the <label>
- ((), {}, '<label for="id_field">Field</label>'),
+ ((), {}, '<label for="id_field">Field:</label>'),
# passing just one argument: overrides the field's label
- (('custom',), {}, '<label for="id_field">custom</label>'),
+ (('custom',), {}, '<label for="id_field">custom:</label>'),
# the overriden label is escaped
- (('custom&',), {}, '<label for="id_field">custom&amp;</label>'),
- ((mark_safe('custom&'),), {}, '<label for="id_field">custom&</label>'),
+ (('custom&',), {}, '<label for="id_field">custom&amp;:</label>'),
+ ((mark_safe('custom&'),), {}, '<label for="id_field">custom&:</label>'),
# Passing attrs to add extra attributes on the <label>
- ((), {'attrs': {'class': 'pretty'}}, '<label for="id_field" class="pretty">Field</label>')
+ ((), {'attrs': {'class': 'pretty'}}, '<label for="id_field" class="pretty">Field:</label>')
]
for args, kwargs, expected in testcases:
@@ -1844,8 +1844,8 @@ class SomeForm(Form):
field = CharField()
boundfield = SomeForm(auto_id='')['field']
- self.assertHTMLEqual(boundfield.label_tag(), 'Field')
- self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&amp;')
+ self.assertHTMLEqual(boundfield.label_tag(), 'Field:')
+ self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&amp;:')
def test_boundfield_label_tag_custom_widget_id_for_label(self):
class CustomIdForLabelTextInput(TextInput):
@@ -1861,5 +1861,5 @@ class SomeForm(Form):
empty = CharField(widget=EmptyIdForLabelTextInput)
form = SomeForm()
- self.assertHTMLEqual(form['custom'].label_tag(), '<label for="custom_id_custom">Custom</label>')
- self.assertHTMLEqual(form['empty'].label_tag(), '<label>Empty</label>')
+ self.assertHTMLEqual(form['custom'].label_tag(), '<label for="custom_id_custom">Custom:</label>')
+ self.assertHTMLEqual(form['empty'].label_tag(), '<label>Empty:</label>')
View
4 tests/forms_tests/tests/test_regressions.py
@@ -45,8 +45,8 @@ class SomeForm(Form):
field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'}))
f = SomeForm()
- self.assertHTMLEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1</label>')
- self.assertHTMLEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2</label>')
+ self.assertHTMLEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1:</label>')
+ self.assertHTMLEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2:</label>')
# Unicode decoding problems...
GENDERS = (('\xc5', 'En tied\xe4'), ('\xf8', 'Mies'), ('\xdf', 'Nainen'))
Please sign in to comment.
Something went wrong with that request. Please try again.