Fixed #26928 -- Changed forms' checked attribute to HTML5 boolean style.

This commit is contained in:
Jon Dufresne 2016-07-21 22:05:17 -07:00 committed by Tim Graham
parent ebed9ee8d5
commit 50e299dbfb
9 changed files with 69 additions and 51 deletions

View File

@ -481,9 +481,7 @@ class CheckboxInput(Widget):
self.check_test = boolean_check if check_test is None else check_test
def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
if self.check_test(value):
final_attrs['checked'] = 'checked'
final_attrs = self.build_attrs(attrs, type='checkbox', name=name, checked=self.check_test(value))
if not (value is True or value is False or value is None or value == ''):
# Only add the 'value' attribute if a value is non-empty.
final_attrs['value'] = force_text(value)
@ -646,9 +644,13 @@ class ChoiceInput(SubWidget):
def tag(self, attrs=None):
attrs = attrs or self.attrs
final_attrs = dict(attrs, type=self.input_type, name=self.name, value=self.choice_value)
if self.is_checked():
final_attrs['checked'] = 'checked'
final_attrs = dict(
attrs,
type=self.input_type,
name=self.name,
value=self.choice_value,
checked=self.is_checked(),
)
return format_html('<input{} />', flatatt(final_attrs))
@property

View File

@ -430,7 +430,7 @@ If the form is bound to data, the HTML output will include that data
appropriately. For example, if a field is represented by an
``<input type="text">``, the data will be in the ``value`` attribute. If a
field is represented by an ``<input type="checkbox">``, then that HTML will
include ``checked="checked"`` if appropriate::
include ``checked`` if appropriate::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
@ -441,7 +441,12 @@ include ``checked="checked"`` if appropriate::
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" required /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" required /></td></tr>
<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" required /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked /></td></tr>
.. versionchanged:: 1.11
The ``checked`` attribute was changed to use HTML5 boolean syntax rather
than ``checked="checked"``.
This default output is a two-column HTML table, with a ``<tr>`` for each field.
Notice the following:
@ -471,6 +476,10 @@ Notice the following:
attributes and ``<label>`` tags are included in the output by default, to
follow best practices, but you can change that behavior.
* The output uses HTML5 syntax, targeting ``<!DOCTYPE html>``. For example,
it uses boolean attributes such as ``checked`` rather than the XHTML style
of ``checked='checked'``.
Although ``<table>`` output is the default output style when you ``print`` a
form, other output styles are available. Each style is available as a method on
a form object, and each rendering method returns a Unicode object.
@ -751,19 +760,19 @@ method you're using::
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" required /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" required /></td></tr>
<tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" required /></td></tr>
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
<tr><th>Cc myself:</th><td><input checked type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" required /></li>
<li>Message: <input type="text" name="message" value="Hi there" required /></li>
<li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" required /></li>
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
<li>Cc myself: <input checked type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" required /></p>
<p>Message: <input type="text" name="message" value="Hi there" required /></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
<p>Sender: <input type="email" name="sender" value="invalid email address" required /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself" /></p>
.. _ref-forms-error-list-format:
@ -789,7 +798,7 @@ Python 2)::
<p>Message: <input type="text" name="message" value="Hi there" required /></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div>
<p>Sender: <input type="email" name="sender" value="invalid email address" required /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
<p>Cc myself: <input checked type="checkbox" name="cc_myself" /></p>
More granular output
====================

View File

@ -11,6 +11,10 @@ A widget is Django's representation of an HTML input element. The widget
handles the rendering of the HTML, and the extraction of data from a GET/POST
dictionary that corresponds to the widget.
The HTML generated by the built-in widgets uses HTML5 syntax, targeting
``<!DOCTYPE html>``. For example, it uses boolean attributes such as ``checked``
rather than the XHTML style of ``checked='checked'``.
.. tip::
Widgets should not be confused with the :doc:`form fields </ref/forms/fields>`.

View File

@ -354,6 +354,9 @@ Miscellaneous
argument. Remove it because it doesn't have an effect in older versions of
Django as these fields alway strip whitespace.
* The ``checked`` attribute rendered by form widgets now uses HTML5 boolean
syntax rather than XHTML's ``checked='checked'``.
.. _deprecated-features-1.11:
Features deprecated in 1.11

View File

@ -423,19 +423,19 @@ class FormsTestCase(SimpleTestCase):
self.assertHTMLEqual(str(f['email']), '<input type="email" name="email" value="test@example.com" required />')
self.assertHTMLEqual(
str(f['get_spam']),
'<input checked="checked" type="checkbox" name="get_spam" required />',
'<input checked type="checkbox" name="get_spam" required />',
)
# 'True' or 'true' should be rendered without a value attribute
f = SignupForm({'email': 'test@example.com', 'get_spam': 'True'}, auto_id=False)
self.assertHTMLEqual(
str(f['get_spam']),
'<input checked="checked" type="checkbox" name="get_spam" required />',
'<input checked type="checkbox" name="get_spam" required />',
)
f = SignupForm({'email': 'test@example.com', 'get_spam': 'true'}, auto_id=False)
self.assertHTMLEqual(
str(f['get_spam']), '<input checked="checked" type="checkbox" name="get_spam" required />')
str(f['get_spam']), '<input checked type="checkbox" name="get_spam" required />')
# A value of 'False' or 'false' should be rendered unchecked
f = SignupForm({'email': 'test@example.com', 'get_spam': 'False'}, auto_id=False)
@ -808,20 +808,20 @@ Java</label></li>
</ul>""")
f = SongForm({'composers': ['J']}, auto_id=False)
self.assertHTMLEqual(str(f['composers']), """<ul>
<li><label><input checked="checked" type="checkbox" name="composers" value="J" /> John Lennon</label></li>
<li><label><input checked type="checkbox" name="composers" value="J" /> John Lennon</label></li>
<li><label><input type="checkbox" name="composers" value="P" /> Paul McCartney</label></li>
</ul>""")
f = SongForm({'composers': ['J', 'P']}, auto_id=False)
self.assertHTMLEqual(str(f['composers']), """<ul>
<li><label><input checked="checked" type="checkbox" name="composers" value="J" /> John Lennon</label></li>
<li><label><input checked="checked" type="checkbox" name="composers" value="P" /> Paul McCartney</label></li>
<li><label><input checked type="checkbox" name="composers" value="J" /> John Lennon</label></li>
<li><label><input checked type="checkbox" name="composers" value="P" /> Paul McCartney</label></li>
</ul>""")
# Test iterating on individual checkboxes in a template
t = Template('{% for checkbox in form.composers %}<div class="mycheckbox">{{ checkbox }}</div>{% endfor %}')
self.assertHTMLEqual(t.render(Context({'form': f})), """<div class="mycheckbox"><label>
<input checked="checked" name="composers" type="checkbox" value="J" /> John Lennon</label></div>
<input checked name="composers" type="checkbox" value="J" /> John Lennon</label></div>
<div class="mycheckbox"><label>
<input checked="checked" name="composers" type="checkbox" value="P" /> Paul McCartney</label></div>""")
<input checked name="composers" type="checkbox" value="P" /> Paul McCartney</label></div>""")
def test_checkbox_auto_id(self):
# Regarding auto_id, CheckboxSelectMultiple is a special case. Each checkbox

View File

@ -44,15 +44,15 @@ class FormsWidgetTests(SimpleTestCase):
)
)
self.assertHTMLEqual('\n'.join(inp_set1), """<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
self.assertHTMLEqual('\n'.join(inp_set1), """<label><input checked type="radio" name="beatle" value="J" /> John</label>
<label><input type="radio" name="beatle" value="P" /> Paul</label>
<label><input type="radio" name="beatle" value="G" /> George</label>
<label><input type="radio" name="beatle" value="R" /> Ringo</label>""")
self.assertHTMLEqual('\n'.join(inp_set2), """<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label><br />
self.assertHTMLEqual('\n'.join(inp_set2), """<label><input checked type="radio" name="beatle" value="J" /> John</label><br />
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
<label><input type="radio" name="beatle" value="G" /> George</label><br />
<label><input type="radio" name="beatle" value="R" /> Ringo</label><br />""")
self.assertHTMLEqual('\n'.join(inp_set3), """<p><input checked="checked" type="radio" name="beatle" value="J" /> John</p>
self.assertHTMLEqual('\n'.join(inp_set3), """<p><input checked type="radio" name="beatle" value="J" /> John</p>
<p><input type="radio" name="beatle" value="P" /> Paul</p>
<p><input type="radio" name="beatle" value="G" /> George</p>
<p><input type="radio" name="beatle" value="R" /> Ringo</p>""")
@ -67,7 +67,7 @@ beatle J R Ringo False""")
self.assertHTMLEqual(str(r[1]), '<label><input type="radio" name="beatle" value="P" /> Paul</label>')
self.assertHTMLEqual(
str(r[0]),
'<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>'
'<label><input checked type="radio" name="beatle" value="J" /> John</label>'
)
self.assertTrue(r[0].is_checked())
self.assertFalse(r[1].is_checked())
@ -91,7 +91,7 @@ beatle J R Ringo False""")
w.render('beatle', 'G'),
"""<label><input type="radio" name="beatle" value="J" /> John</label><br />
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
<label><input checked type="radio" name="beatle" value="G" /> George</label><br />
<label><input type="radio" name="beatle" value="R" /> Ringo</label>"""
)
@ -103,7 +103,7 @@ beatle J R Ringo False""")
w.render('beatle', 'G'),
"""<label><input type="radio" name="beatle" value="J" /> John</label><br />
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
<label><input checked type="radio" name="beatle" value="G" /> George</label><br />
<label><input type="radio" name="beatle" value="R" /> Ringo</label>"""
)
@ -118,7 +118,7 @@ beatle J R Ringo False""")
self.assertHTMLEqual(
output,
"""<div id="bar">
<p><label for="bar_0"><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label></p>
<p><label for="bar_0"><input checked type="radio" id="bar_0" value="J" name="beatle" /> John</label></p>
<p><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></p>
<p><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></p>
<p><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></p>
@ -133,18 +133,18 @@ beatle J R Ringo False""")
choices=zip('abc', 'ABC')
).subwidgets('letters', list('ac'))
),
"""<input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" />
"""<input checked type="checkbox" name="letters" value="a" id="abc_0" />
<input type="checkbox" name="letters" value="b" id="abc_1" />
<input checked="checked" type="checkbox" name="letters" value="c" id="abc_2" />""")
<input checked type="checkbox" name="letters" value="c" id="abc_2" />""")
# Each subwidget tag does not get an ID if the widget does not have an ID specified
self.assertHTMLEqual(
"\n".join(c.tag() for c in CheckboxSelectMultiple(
choices=zip('abc', 'ABC'),
).subwidgets('letters', list('ac'))),
"""<input checked="checked" type="checkbox" name="letters" value="a" />
"""<input checked type="checkbox" name="letters" value="a" />
<input type="checkbox" name="letters" value="b" />
<input checked="checked" type="checkbox" name="letters" value="c" />""")
<input checked type="checkbox" name="letters" value="c" />""")
# The id_for_label property of the subwidget should return the ID that is used on the subwidget's tag
self.assertHTMLEqual(

View File

@ -18,7 +18,7 @@ class CheckboxInputTest(WidgetTest):
def test_render_true(self):
self.check_html(
self.widget, 'is_cool', True,
html='<input checked="checked" type="checkbox" name="is_cool" />'
html='<input checked type="checkbox" name="is_cool" />'
)
def test_render_value(self):
@ -28,7 +28,7 @@ class CheckboxInputTest(WidgetTest):
"""
self.check_html(
self.widget, 'is_cool', 'foo',
html='<input checked="checked" type="checkbox" name="is_cool" value="foo" />',
html='<input checked type="checkbox" name="is_cool" value="foo" />',
)
def test_render_int(self):
@ -37,11 +37,11 @@ class CheckboxInputTest(WidgetTest):
"""
self.check_html(
self.widget, 'is_cool', 0,
html='<input checked="checked" type="checkbox" name="is_cool" value="0" />',
html='<input checked type="checkbox" name="is_cool" value="0" />',
)
self.check_html(
self.widget, 'is_cool', 1,
html='<input checked="checked" type="checkbox" name="is_cool" value="1" />',
html='<input checked type="checkbox" name="is_cool" value="1" />',
)
def test_render_check_test(self):
@ -54,13 +54,13 @@ class CheckboxInputTest(WidgetTest):
'<input type="checkbox" name="greeting" />'
))
self.check_html(widget, 'greeting', 'hello', html=(
'<input checked="checked" type="checkbox" name="greeting" value="hello" />'
'<input checked type="checkbox" name="greeting" value="hello" />'
))
self.check_html(widget, 'greeting', 'hello there', html=(
'<input checked="checked" type="checkbox" name="greeting" value="hello there" />'
'<input checked type="checkbox" name="greeting" value="hello there" />'
))
self.check_html(widget, 'greeting', 'hello & goodbye', html=(
'<input checked="checked" type="checkbox" name="greeting" value="hello &amp; goodbye" />'
'<input checked type="checkbox" name="greeting" value="hello &amp; goodbye" />'
))
def test_render_check_exception(self):

View File

@ -9,7 +9,7 @@ class CheckboxSelectMultipleTest(WidgetTest):
def test_render_value(self):
self.check_html(self.widget(choices=self.beatles), 'beatles', ['J'], html=(
"""<ul>
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
<li><label><input checked type="checkbox" name="beatles" value="J" /> John</label></li>
<li><label><input type="checkbox" name="beatles" value="P" /> Paul</label></li>
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
@ -19,8 +19,8 @@ class CheckboxSelectMultipleTest(WidgetTest):
def test_render_value_multiple(self):
self.check_html(self.widget(choices=self.beatles), 'beatles', ['J', 'P'], html=(
"""<ul>
<li><label><input checked="checked" type="checkbox" name="beatles" value="J" /> John</label></li>
<li><label><input checked="checked" type="checkbox" name="beatles" value="P" /> Paul</label></li>
<li><label><input checked type="checkbox" name="beatles" value="J" /> John</label></li>
<li><label><input checked type="checkbox" name="beatles" value="P" /> Paul</label></li>
<li><label><input type="checkbox" name="beatles" value="G" /> George</label></li>
<li><label><input type="checkbox" name="beatles" value="R" /> Ringo</label></li>
</ul>"""
@ -53,7 +53,7 @@ class CheckboxSelectMultipleTest(WidgetTest):
<li>Audio<ul id="media_1">
<li>
<label for="media_1_0">
<input checked="checked" id="media_1_0" name="nestchoice" type="checkbox" value="vinyl" /> Vinyl
<input checked id="media_1_0" name="nestchoice" type="checkbox" value="vinyl" /> Vinyl
</label>
</li>
<li>
@ -66,7 +66,7 @@ class CheckboxSelectMultipleTest(WidgetTest):
</li>
<li>
<label for="media_2_1">
<input checked="checked" id="media_2_1" name="nestchoice" type="checkbox" value="dvd" /> DVD
<input checked id="media_2_1" name="nestchoice" type="checkbox" value="dvd" /> DVD
</label>
</li>
</ul></li>
@ -85,11 +85,11 @@ class CheckboxSelectMultipleTest(WidgetTest):
html = """
<ul id="abc">
<li>
<label for="abc_0"><input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" /> A</label>
<label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0" /> A</label>
</li>
<li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1" /> B</label></li>
<li>
<label for="abc_2"><input checked="checked" type="checkbox" name="letters" value="c" id="abc_2" /> C</label>
<label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2" /> C</label>
</li>
</ul>
"""
@ -103,11 +103,11 @@ class CheckboxSelectMultipleTest(WidgetTest):
html = """
<ul id="abc">
<li>
<label for="abc_0"><input checked="checked" type="checkbox" name="letters" value="a" id="abc_0" /> A</label>
<label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0" /> A</label>
</li>
<li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1" /> B</label></li>
<li>
<label for="abc_2"><input checked="checked" type="checkbox" name="letters" value="c" id="abc_2" /> C</label>
<label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2" /> C</label>
</li>
</ul>
"""

View File

@ -9,7 +9,7 @@ class RadioSelectTest(WidgetTest):
def test_render(self):
self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', html=(
"""<ul>
<li><label><input checked="checked" type="radio" name="beatle" value="J" /> John</label></li>
<li><label><input checked type="radio" name="beatle" value="J" /> John</label></li>
<li><label><input type="radio" name="beatle" value="P" /> Paul</label></li>
<li><label><input type="radio" name="beatle" value="G" /> George</label></li>
<li><label><input type="radio" name="beatle" value="R" /> Ringo</label></li>
@ -37,7 +37,7 @@ class RadioSelectTest(WidgetTest):
<li><label for="media_2_0"><input id="media_2_0" name="nestchoice" type="radio" value="vhs" /> VHS</label></li>
<li>
<label for="media_2_1">
<input checked="checked" id="media_2_1" name="nestchoice" type="radio" value="dvd" /> DVD
<input checked id="media_2_1" name="nestchoice" type="radio" value="dvd" /> DVD
</label>
</li>
</ul></li>
@ -57,7 +57,7 @@ class RadioSelectTest(WidgetTest):
html = """
<ul id="foo">
<li>
<label for="foo_0"><input checked="checked" type="radio" id="foo_0" value="J" name="beatle" /> John</label>
<label for="foo_0"><input checked type="radio" id="foo_0" value="J" name="beatle" /> John</label>
</li>
<li><label for="foo_1"><input type="radio" id="foo_1" value="P" name="beatle" /> Paul</label></li>
<li><label for="foo_2"><input type="radio" id="foo_2" value="G" name="beatle" /> George</label></li>
@ -74,7 +74,7 @@ class RadioSelectTest(WidgetTest):
html = """
<ul id="bar">
<li>
<label for="bar_0"><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label>
<label for="bar_0"><input checked type="radio" id="bar_0" value="J" name="beatle" /> John</label>
</li>
<li><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li>
<li><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li>