Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
Gabe Jackson authored June 08, 2012 timgraham committed June 10, 2013
9  django/forms/forms.py
@@ -170,11 +170,6 @@ def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_
170 170
 
171 171
                 if bf.label:
172 172
                     label = conditional_escape(force_text(bf.label))
173  
-                    # Only add the suffix if the label does not end in
174  
-                    # punctuation.
175  
-                    if self.label_suffix:
176  
-                        if label[-1] not in ':?.!':
177  
-                            label = format_html('{0}{1}', label, self.label_suffix)
178 173
                     label = bf.label_tag(label) or ''
179 174
                 else:
180 175
                     label = ''
@@ -522,6 +517,10 @@ def label_tag(self, contents=None, attrs=None):
522 517
         If attrs are given, they're used as HTML attributes on the <label> tag.
523 518
         """
524 519
         contents = contents or self.label
  520
+        # Only add the suffix if the label does not end in punctuation.
  521
+        if self.form.label_suffix:
  522
+            if contents[-1] not in ':?.!':
  523
+                contents = format_html('{0}{1}', contents, self.form.label_suffix)
525 524
         widget = self.field.widget
526 525
         id_ = widget.attrs.get('id') or self.auto_id
527 526
         if id_:
9  docs/ref/forms/api.txt
@@ -498,6 +498,8 @@ include ``%s`` -- then the library will act as if ``auto_id`` is ``True``.
498 498
 
499 499
 By default, ``auto_id`` is set to the string ``'id_%s'``.
500 500
 
  501
+.. attribute:: Form.label_suffix
  502
+
501 503
 Normally, a colon (``:``) will be appended after any label name when a form is
502 504
 rendered. It's possible to change the colon to another character, or omit it
503 505
 entirely, using the ``label_suffix`` parameter::
@@ -650,12 +652,17 @@ To separately render the label tag of a form field, you can call its
650 652
 
651 653
     >>> f = ContactForm(data)
652 654
     >>> print(f['message'].label_tag())
653  
-    <label for="id_message">Message</label>
  655
+    <label for="id_message">Message:</label>
654 656
 
655 657
 Optionally, you can provide the ``contents`` parameter which will replace the
656 658
 auto-generated label tag. An optional ``attrs`` dictionary may contain
657 659
 additional attributes for the ``<label>`` tag.
658 660
 
  661
+.. versionchanged:: 1.6
  662
+
  663
+    The label now includes the form's :attr:`~django.forms.Form.label_suffix`
  664
+    (a semicolon, by default).
  665
+
659 666
 .. method:: BoundField.css_classes()
660 667
 
661 668
 When you use Django's rendering shortcuts, CSS classes are used to
31  docs/releases/1.6.txt
@@ -581,6 +581,37 @@ It is still possible to convert the fetched rows to ``Model`` objects
581 581
 lazily by using the :meth:`~django.db.models.query.QuerySet.iterator()`
582 582
 method.
583 583
 
  584
+:meth:`BoundField.label_tag<django.forms.BoundField.label_tag>` now includes the form's :attr:`~django.forms.Form.label_suffix`
  585
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  586
+
  587
+This is consistent with how methods like
  588
+:meth:`Form.as_p<django.forms.Form.as_p>` and
  589
+:meth:`Form.as_ul<django.forms.Form.as_ul>` render labels.
  590
+
  591
+If you manually render ``label_tag`` in your templates:
  592
+
  593
+.. code-block:: html+django
  594
+
  595
+    {{ form.my_field.label_tag }}: {{ form.my_field }}
  596
+
  597
+you'll want to remove the semicolon (or whatever other separator you may be
  598
+using) to avoid duplicating it when upgrading to Django 1.6. The following
  599
+template in Django 1.6 will render identically to the above template in Django
  600
+1.5, except that the semicolon will appear inside the ``<label>`` element.
  601
+
  602
+.. code-block:: html+django
  603
+
  604
+     {{ form.my_field.label_tag }} {{ form.my_field }}
  605
+
  606
+will render something like:
  607
+
  608
+.. code-block:: html
  609
+
  610
+    <label for="id_my_field">My Field:</label> <input id="id_my_field" type="text" name="my_field" />
  611
+
  612
+If you want to keep the current behavior of rendering ``label_tag`` without
  613
+the ``label_suffix``, instantiate the form ``label_suffix=''``.
  614
+
584 615
 Miscellaneous
585 616
 ~~~~~~~~~~~~~
586 617
 
16  docs/topics/forms/index.txt
@@ -302,7 +302,7 @@ loop::
302 302
         {% for field in form %}
303 303
             <div class="fieldWrapper">
304 304
                 {{ field.errors }}
305  
-                {{ field.label_tag }}: {{ field }}
  305
+                {{ field.label_tag }} {{ field }}
306 306
             </div>
307 307
         {% endfor %}
308 308
         <p><input type="submit" value="Send message" /></p>
@@ -316,8 +316,14 @@ attributes, which can be useful in your templates:
316 316
     The label of the field, e.g. ``Email address``.
317 317
 
318 318
 ``{{ field.label_tag }}``
319  
-    The field's label wrapped in the appropriate HTML ``<label>`` tag,
320  
-    e.g. ``<label for="id_email">Email address</label>``
  319
+    The field's label wrapped in the appropriate HTML ``<label>`` tag.
  320
+
  321
+    .. versionchanged:: 1.6
  322
+
  323
+        This includes the form's :attr:`~django.forms.Form.label_suffix`. For
  324
+        example, the default ``label_suffix`` is a semicolon::
  325
+
  326
+            <label for="id_email">Email address:</label>
321 327
 
322 328
 ``{{ field.value }}``
323 329
     The value of the field. e.g ``someone@example.com``
@@ -375,7 +381,7 @@ these two methods::
375 381
         {% for field in form.visible_fields %}
376 382
             <div class="fieldWrapper">
377 383
                 {{ field.errors }}
378  
-                {{ field.label_tag }}: {{ field }}
  384
+                {{ field.label_tag }} {{ field }}
379 385
             </div>
380 386
         {% endfor %}
381 387
         <p><input type="submit" value="Send message" /></p>
@@ -403,7 +409,7 @@ using the :ttag:`include` tag to reuse it in other templates::
403 409
     {% for field in form %}
404 410
         <div class="fieldWrapper">
405 411
             {{ field.errors }}
406  
-            {{ field.label_tag }}: {{ field }}
  412
+            {{ field.label_tag }} {{ field }}
407 413
         </div>
408 414
     {% endfor %}
409 415
 
2  docs/topics/forms/modelforms.txt
@@ -907,7 +907,7 @@ Third, you can manually render each field::
907 907
         {{ formset.management_form }}
908 908
         {% for form in formset %}
909 909
             {% for field in form %}
910  
-                {{ field.label_tag }}: {{ field }}
  910
+                {{ field.label_tag }} {{ field }}
911 911
             {% endfor %}
912 912
         {% endfor %}
913 913
     </form>
4  tests/admin_util/tests.py
@@ -301,7 +301,7 @@ class MyForm(forms.Form):
301 301
         self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
302 302
                              '<label for="id_text" class="required inline"><i>text</i>:</label>')
303 303
         self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
304  
-                             '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i></label>')
  304
+                             '<label for="id_cb" class="vCheckboxLabel required inline"><i>cb</i>:</label>')
305 305
 
306 306
         # normal strings needs to be escaped
307 307
         class MyForm(forms.Form):
@@ -312,7 +312,7 @@ class MyForm(forms.Form):
312 312
         self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
313 313
                              '<label for="id_text" class="required inline">&amp;text:</label>')
314 314
         self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
315  
-                             '<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb</label>')
  315
+                             '<label for="id_cb" class="vCheckboxLabel required inline">&amp;cb:</label>')
316 316
 
317 317
     def test_flatten_fieldsets(self):
318 318
         """
36  tests/forms_tests/tests/test_forms.py
@@ -1589,9 +1589,9 @@ def clean(self):
1589 1589
         # Recall from above that passing the "auto_id" argument to a Form gives each
1590 1590
         # field an "id" attribute.
1591 1591
         t = Template('''<form action="">
1592  
-<p>{{ form.username.label_tag }}: {{ form.username }}</p>
1593  
-<p>{{ form.password1.label_tag }}: {{ form.password1 }}</p>
1594  
-<p>{{ form.password2.label_tag }}: {{ form.password2 }}</p>
  1592
+<p>{{ form.username.label_tag }} {{ form.username }}</p>
  1593
+<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>
  1594
+<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>
1595 1595
 <input type="submit" />
1596 1596
 </form>''')
1597 1597
         self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
@@ -1601,18 +1601,18 @@ def clean(self):
1601 1601
 <input type="submit" />
1602 1602
 </form>""")
1603 1603
         self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id='id_%s')})), """<form action="">
1604  
-<p><label for="id_username">Username</label>: <input id="id_username" type="text" name="username" maxlength="10" /></p>
1605  
-<p><label for="id_password1">Password1</label>: <input type="password" name="password1" id="id_password1" /></p>
1606  
-<p><label for="id_password2">Password2</label>: <input type="password" name="password2" id="id_password2" /></p>
  1604
+<p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>
  1605
+<p><label for="id_password1">Password1:</label> <input type="password" name="password1" id="id_password1" /></p>
  1606
+<p><label for="id_password2">Password2:</label> <input type="password" name="password2" id="id_password2" /></p>
1607 1607
 <input type="submit" />
1608 1608
 </form>""")
1609 1609
 
1610 1610
         # User form.[field].help_text to output a field's help text. If the given field
1611 1611
         # does not have help text, nothing will be output.
1612 1612
         t = Template('''<form action="">
1613  
-<p>{{ form.username.label_tag }}: {{ form.username }}<br />{{ form.username.help_text }}</p>
1614  
-<p>{{ form.password1.label_tag }}: {{ form.password1 }}</p>
1615  
-<p>{{ form.password2.label_tag }}: {{ form.password2 }}</p>
  1613
+<p>{{ form.username.label_tag }} {{ form.username }}<br />{{ form.username.help_text }}</p>
  1614
+<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>
  1615
+<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>
1616 1616
 <input type="submit" />
1617 1617
 </form>''')
1618 1618
         self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
@@ -1819,17 +1819,17 @@ class SomeForm(Form):
1819 1819
 
1820 1820
         testcases = [  # (args, kwargs, expected)
1821 1821
             # without anything: just print the <label>
1822  
-            ((), {}, '<label for="id_field">Field</label>'),
  1822
+            ((), {}, '<label for="id_field">Field:</label>'),
1823 1823
 
1824 1824
             # passing just one argument: overrides the field's label
1825  
-            (('custom',), {}, '<label for="id_field">custom</label>'),
  1825
+            (('custom',), {}, '<label for="id_field">custom:</label>'),
1826 1826
 
1827 1827
             # the overriden label is escaped
1828  
-            (('custom&',), {}, '<label for="id_field">custom&amp;</label>'),
1829  
-            ((mark_safe('custom&'),), {}, '<label for="id_field">custom&</label>'),
  1828
+            (('custom&',), {}, '<label for="id_field">custom&amp;:</label>'),
  1829
+            ((mark_safe('custom&'),), {}, '<label for="id_field">custom&:</label>'),
1830 1830
 
1831 1831
             # Passing attrs to add extra attributes on the <label>
1832  
-            ((), {'attrs': {'class': 'pretty'}}, '<label for="id_field" class="pretty">Field</label>')
  1832
+            ((), {'attrs': {'class': 'pretty'}}, '<label for="id_field" class="pretty">Field:</label>')
1833 1833
         ]
1834 1834
 
1835 1835
         for args, kwargs, expected in testcases:
@@ -1844,8 +1844,8 @@ class SomeForm(Form):
1844 1844
             field = CharField()
1845 1845
         boundfield = SomeForm(auto_id='')['field']
1846 1846
 
1847  
-        self.assertHTMLEqual(boundfield.label_tag(), 'Field')
1848  
-        self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&amp;')
  1847
+        self.assertHTMLEqual(boundfield.label_tag(), 'Field:')
  1848
+        self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&amp;:')
1849 1849
 
1850 1850
     def test_boundfield_label_tag_custom_widget_id_for_label(self):
1851 1851
         class CustomIdForLabelTextInput(TextInput):
@@ -1861,5 +1861,5 @@ class SomeForm(Form):
1861 1861
             empty = CharField(widget=EmptyIdForLabelTextInput)
1862 1862
 
1863 1863
         form = SomeForm()
1864  
-        self.assertHTMLEqual(form['custom'].label_tag(), '<label for="custom_id_custom">Custom</label>')
1865  
-        self.assertHTMLEqual(form['empty'].label_tag(), '<label>Empty</label>')
  1864
+        self.assertHTMLEqual(form['custom'].label_tag(), '<label for="custom_id_custom">Custom:</label>')
  1865
+        self.assertHTMLEqual(form['empty'].label_tag(), '<label>Empty:</label>')
4  tests/forms_tests/tests/test_regressions.py
@@ -45,8 +45,8 @@ class SomeForm(Form):
45 45
             field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'}))
46 46
 
47 47
         f = SomeForm()
48  
-        self.assertHTMLEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1</label>')
49  
-        self.assertHTMLEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2</label>')
  48
+        self.assertHTMLEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1:</label>')
  49
+        self.assertHTMLEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2:</label>')
50 50
 
51 51
         # Unicode decoding problems...
52 52
         GENDERS = (('\xc5', 'En tied\xe4'), ('\xf8', 'Mies'), ('\xdf', 'Nainen'))

0 notes on commit 584bd14

Please sign in to comment.
Something went wrong with that request. Please try again.