Fixed #18134 -- BoundField.label_tag now includes the form's label_suffix
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.
This commit is contained in:
parent
a643e4d200
commit
584bd14dcf
|
@ -170,11 +170,6 @@ class BaseForm(object):
|
||||||
|
|
||||||
if bf.label:
|
if bf.label:
|
||||||
label = conditional_escape(force_text(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 ''
|
label = bf.label_tag(label) or ''
|
||||||
else:
|
else:
|
||||||
label = ''
|
label = ''
|
||||||
|
@ -522,6 +517,10 @@ class BoundField(object):
|
||||||
If attrs are given, they're used as HTML attributes on the <label> tag.
|
If attrs are given, they're used as HTML attributes on the <label> tag.
|
||||||
"""
|
"""
|
||||||
contents = contents or self.label
|
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
|
widget = self.field.widget
|
||||||
id_ = widget.attrs.get('id') or self.auto_id
|
id_ = widget.attrs.get('id') or self.auto_id
|
||||||
if id_:
|
if id_:
|
||||||
|
|
|
@ -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'``.
|
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
|
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
|
rendered. It's possible to change the colon to another character, or omit it
|
||||||
entirely, using the ``label_suffix`` parameter::
|
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)
|
>>> f = ContactForm(data)
|
||||||
>>> print(f['message'].label_tag())
|
>>> 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
|
Optionally, you can provide the ``contents`` parameter which will replace the
|
||||||
auto-generated label tag. An optional ``attrs`` dictionary may contain
|
auto-generated label tag. An optional ``attrs`` dictionary may contain
|
||||||
additional attributes for the ``<label>`` tag.
|
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()
|
.. method:: BoundField.css_classes()
|
||||||
|
|
||||||
When you use Django's rendering shortcuts, CSS classes are used to
|
When you use Django's rendering shortcuts, CSS classes are used to
|
||||||
|
|
|
@ -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()`
|
lazily by using the :meth:`~django.db.models.query.QuerySet.iterator()`
|
||||||
method.
|
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
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,7 @@ loop::
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
<div class="fieldWrapper">
|
<div class="fieldWrapper">
|
||||||
{{ field.errors }}
|
{{ field.errors }}
|
||||||
{{ field.label_tag }}: {{ field }}
|
{{ field.label_tag }} {{ field }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<p><input type="submit" value="Send message" /></p>
|
<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``.
|
The label of the field, e.g. ``Email address``.
|
||||||
|
|
||||||
``{{ field.label_tag }}``
|
``{{ field.label_tag }}``
|
||||||
The field's label wrapped in the appropriate HTML ``<label>`` tag,
|
The field's label wrapped in the appropriate HTML ``<label>`` tag.
|
||||||
e.g. ``<label for="id_email">Email address</label>``
|
|
||||||
|
.. 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 }}``
|
``{{ field.value }}``
|
||||||
The value of the field. e.g ``someone@example.com``
|
The value of the field. e.g ``someone@example.com``
|
||||||
|
@ -375,7 +381,7 @@ these two methods::
|
||||||
{% for field in form.visible_fields %}
|
{% for field in form.visible_fields %}
|
||||||
<div class="fieldWrapper">
|
<div class="fieldWrapper">
|
||||||
{{ field.errors }}
|
{{ field.errors }}
|
||||||
{{ field.label_tag }}: {{ field }}
|
{{ field.label_tag }} {{ field }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<p><input type="submit" value="Send message" /></p>
|
<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 %}
|
{% for field in form %}
|
||||||
<div class="fieldWrapper">
|
<div class="fieldWrapper">
|
||||||
{{ field.errors }}
|
{{ field.errors }}
|
||||||
{{ field.label_tag }}: {{ field }}
|
{{ field.label_tag }} {{ field }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -907,7 +907,7 @@ Third, you can manually render each field::
|
||||||
{{ formset.management_form }}
|
{{ formset.management_form }}
|
||||||
{% for form in formset %}
|
{% for form in formset %}
|
||||||
{% for field in form %}
|
{% for field in form %}
|
||||||
{{ field.label_tag }}: {{ field }}
|
{{ field.label_tag }} {{ field }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -301,7 +301,7 @@ class UtilTests(SimpleTestCase):
|
||||||
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
|
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
|
||||||
'<label for="id_text" class="required inline"><i>text</i>:</label>')
|
'<label for="id_text" class="required inline"><i>text</i>:</label>')
|
||||||
self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
|
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
|
# normal strings needs to be escaped
|
||||||
class MyForm(forms.Form):
|
class MyForm(forms.Form):
|
||||||
|
@ -312,7 +312,7 @@ class UtilTests(SimpleTestCase):
|
||||||
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
|
self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(),
|
||||||
'<label for="id_text" class="required inline">&text:</label>')
|
'<label for="id_text" class="required inline">&text:</label>')
|
||||||
self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
|
self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(),
|
||||||
'<label for="id_cb" class="vCheckboxLabel required inline">&cb</label>')
|
'<label for="id_cb" class="vCheckboxLabel required inline">&cb:</label>')
|
||||||
|
|
||||||
def test_flatten_fieldsets(self):
|
def test_flatten_fieldsets(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1589,9 +1589,9 @@ class FormsTestCase(TestCase):
|
||||||
# Recall from above that passing the "auto_id" argument to a Form gives each
|
# Recall from above that passing the "auto_id" argument to a Form gives each
|
||||||
# field an "id" attribute.
|
# field an "id" attribute.
|
||||||
t = Template('''<form action="">
|
t = Template('''<form action="">
|
||||||
<p>{{ form.username.label_tag }}: {{ form.username }}</p>
|
<p>{{ form.username.label_tag }} {{ form.username }}</p>
|
||||||
<p>{{ form.password1.label_tag }}: {{ form.password1 }}</p>
|
<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>
|
||||||
<p>{{ form.password2.label_tag }}: {{ form.password2 }}</p>
|
<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>''')
|
</form>''')
|
||||||
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
|
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
|
||||||
|
@ -1601,18 +1601,18 @@ class FormsTestCase(TestCase):
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>""")
|
</form>""")
|
||||||
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id='id_%s')})), """<form action="">
|
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_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_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_password2">Password2:</label> <input type="password" name="password2" id="id_password2" /></p>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>""")
|
</form>""")
|
||||||
|
|
||||||
# User form.[field].help_text to output a field's help text. If the given field
|
# 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.
|
# does not have help text, nothing will be output.
|
||||||
t = Template('''<form action="">
|
t = Template('''<form action="">
|
||||||
<p>{{ form.username.label_tag }}: {{ form.username }}<br />{{ form.username.help_text }}</p>
|
<p>{{ form.username.label_tag }} {{ form.username }}<br />{{ form.username.help_text }}</p>
|
||||||
<p>{{ form.password1.label_tag }}: {{ form.password1 }}</p>
|
<p>{{ form.password1.label_tag }} {{ form.password1 }}</p>
|
||||||
<p>{{ form.password2.label_tag }}: {{ form.password2 }}</p>
|
<p>{{ form.password2.label_tag }} {{ form.password2 }}</p>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>''')
|
</form>''')
|
||||||
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
|
self.assertHTMLEqual(t.render(Context({'form': UserRegistration(auto_id=False)})), """<form action="">
|
||||||
|
@ -1819,17 +1819,17 @@ class FormsTestCase(TestCase):
|
||||||
|
|
||||||
testcases = [ # (args, kwargs, expected)
|
testcases = [ # (args, kwargs, expected)
|
||||||
# without anything: just print the <label>
|
# 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
|
# 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
|
# the overriden label is escaped
|
||||||
(('custom&',), {}, '<label for="id_field">custom&</label>'),
|
(('custom&',), {}, '<label for="id_field">custom&:</label>'),
|
||||||
((mark_safe('custom&'),), {}, '<label for="id_field">custom&</label>'),
|
((mark_safe('custom&'),), {}, '<label for="id_field">custom&:</label>'),
|
||||||
|
|
||||||
# Passing attrs to add extra attributes on the <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:
|
for args, kwargs, expected in testcases:
|
||||||
|
@ -1844,8 +1844,8 @@ class FormsTestCase(TestCase):
|
||||||
field = CharField()
|
field = CharField()
|
||||||
boundfield = SomeForm(auto_id='')['field']
|
boundfield = SomeForm(auto_id='')['field']
|
||||||
|
|
||||||
self.assertHTMLEqual(boundfield.label_tag(), 'Field')
|
self.assertHTMLEqual(boundfield.label_tag(), 'Field:')
|
||||||
self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&')
|
self.assertHTMLEqual(boundfield.label_tag('Custom&'), 'Custom&:')
|
||||||
|
|
||||||
def test_boundfield_label_tag_custom_widget_id_for_label(self):
|
def test_boundfield_label_tag_custom_widget_id_for_label(self):
|
||||||
class CustomIdForLabelTextInput(TextInput):
|
class CustomIdForLabelTextInput(TextInput):
|
||||||
|
@ -1861,5 +1861,5 @@ class FormsTestCase(TestCase):
|
||||||
empty = CharField(widget=EmptyIdForLabelTextInput)
|
empty = CharField(widget=EmptyIdForLabelTextInput)
|
||||||
|
|
||||||
form = SomeForm()
|
form = SomeForm()
|
||||||
self.assertHTMLEqual(form['custom'].label_tag(), '<label for="custom_id_custom">Custom</label>')
|
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['empty'].label_tag(), '<label>Empty:</label>')
|
||||||
|
|
|
@ -45,8 +45,8 @@ class FormsRegressionsTestCase(TransRealMixin, TestCase):
|
||||||
field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'}))
|
field_2 = CharField(max_length=10, label=ugettext_lazy('field_2'), widget=TextInput(attrs={'id': 'field_2_id'}))
|
||||||
|
|
||||||
f = SomeForm()
|
f = SomeForm()
|
||||||
self.assertHTMLEqual(f['field_1'].label_tag(), '<label for="id_field_1">field_1</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>')
|
self.assertHTMLEqual(f['field_2'].label_tag(), '<label for="field_2_id">field_2:</label>')
|
||||||
|
|
||||||
# Unicode decoding problems...
|
# Unicode decoding problems...
|
||||||
GENDERS = (('\xc5', 'En tied\xe4'), ('\xf8', 'Mies'), ('\xdf', 'Nainen'))
|
GENDERS = (('\xc5', 'En tied\xe4'), ('\xf8', 'Mies'), ('\xdf', 'Nainen'))
|
||||||
|
|
Loading…
Reference in New Issue