Fixed #13316 -- Modified the default behavior of PasswordInput to prevent reflecting passwords on form failure. Thanks to clouserw for the report.
Although this changes nothing at a functional level, this is BACKWARDS INCOMPATIBLE from a UX perspective for anyone that wants passwords to be reflected to the user on form failure. See the 1.3 release notes for details. git-svn-id: http://code.djangoproject.com/svn/django/trunk@13498 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
69d1e71fad
commit
d3ba8cb88b
|
@ -229,7 +229,7 @@ class TextInput(Input):
|
||||||
class PasswordInput(Input):
|
class PasswordInput(Input):
|
||||||
input_type = 'password'
|
input_type = 'password'
|
||||||
|
|
||||||
def __init__(self, attrs=None, render_value=True):
|
def __init__(self, attrs=None, render_value=False):
|
||||||
super(PasswordInput, self).__init__(attrs)
|
super(PasswordInput, self).__init__(attrs)
|
||||||
self.render_value = render_value
|
self.render_value = render_value
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ Widgets
|
||||||
|
|
||||||
.. module:: django.forms.widgets
|
.. module:: django.forms.widgets
|
||||||
:synopsis: Django's built-in form widgets.
|
:synopsis: Django's built-in form widgets.
|
||||||
|
|
||||||
.. currentmodule:: django.forms
|
.. currentmodule:: django.forms
|
||||||
|
|
||||||
A widget is Django's representation of a HTML input element. The widget
|
A widget is Django's representation of a HTML input element. The widget
|
||||||
|
@ -29,7 +29,12 @@ commonly used groups of widgets:
|
||||||
.. attribute:: PasswordInput.render_value
|
.. attribute:: PasswordInput.render_value
|
||||||
|
|
||||||
Determines whether the widget will have a value filled in when the
|
Determines whether the widget will have a value filled in when the
|
||||||
form is re-displayed after a validation error (default is ``True``).
|
form is re-displayed after a validation error (default is ``False``).
|
||||||
|
|
||||||
|
.. versionchanged:: 1.3
|
||||||
|
The default value for
|
||||||
|
:attr:`~PasswordInput.render_value` was
|
||||||
|
changed from ``True`` to ``False``
|
||||||
|
|
||||||
.. class:: HiddenInput
|
.. class:: HiddenInput
|
||||||
|
|
||||||
|
@ -50,7 +55,7 @@ commonly used groups of widgets:
|
||||||
Date input as a simple text box: ``<input type='text' ...>``
|
Date input as a simple text box: ``<input type='text' ...>``
|
||||||
|
|
||||||
Takes one optional argument:
|
Takes one optional argument:
|
||||||
|
|
||||||
.. attribute:: DateInput.format
|
.. attribute:: DateInput.format
|
||||||
|
|
||||||
The format in which this field's initial value will be displayed.
|
The format in which this field's initial value will be displayed.
|
||||||
|
@ -64,11 +69,11 @@ commonly used groups of widgets:
|
||||||
Date/time input as a simple text box: ``<input type='text' ...>``
|
Date/time input as a simple text box: ``<input type='text' ...>``
|
||||||
|
|
||||||
Takes one optional argument:
|
Takes one optional argument:
|
||||||
|
|
||||||
.. attribute:: DateTimeInput.format
|
.. attribute:: DateTimeInput.format
|
||||||
|
|
||||||
The format in which this field's initial value will be displayed.
|
The format in which this field's initial value will be displayed.
|
||||||
|
|
||||||
If no ``format`` argument is provided, the default format is ``'%Y-%m-%d
|
If no ``format`` argument is provided, the default format is ``'%Y-%m-%d
|
||||||
%H:%M:%S'``.
|
%H:%M:%S'``.
|
||||||
|
|
||||||
|
@ -77,11 +82,11 @@ commonly used groups of widgets:
|
||||||
Time input as a simple text box: ``<input type='text' ...>``
|
Time input as a simple text box: ``<input type='text' ...>``
|
||||||
|
|
||||||
Takes one optional argument:
|
Takes one optional argument:
|
||||||
|
|
||||||
.. attribute:: TimeInput.format
|
.. attribute:: TimeInput.format
|
||||||
|
|
||||||
The format in which this field's initial value will be displayed.
|
The format in which this field's initial value will be displayed.
|
||||||
|
|
||||||
If no ``format`` argument is provided, the default format is ``'%H:%M:%S'``.
|
If no ``format`` argument is provided, the default format is ``'%H:%M:%S'``.
|
||||||
|
|
||||||
.. versionchanged:: 1.1
|
.. versionchanged:: 1.1
|
||||||
|
@ -98,15 +103,15 @@ commonly used groups of widgets:
|
||||||
Takes one optional argument:
|
Takes one optional argument:
|
||||||
|
|
||||||
.. attribute:: CheckboxInput.check_test
|
.. attribute:: CheckboxInput.check_test
|
||||||
|
|
||||||
A callable that takes the value of the CheckBoxInput
|
A callable that takes the value of the CheckBoxInput
|
||||||
and returns ``True`` if the checkbox should be checked for
|
and returns ``True`` if the checkbox should be checked for
|
||||||
that value.
|
that value.
|
||||||
|
|
||||||
.. class:: Select
|
.. class:: Select
|
||||||
|
|
||||||
Select widget: ``<select><option ...>...</select>``
|
Select widget: ``<select><option ...>...</select>``
|
||||||
|
|
||||||
Requires that your field provides :attr:`~Field.choices`.
|
Requires that your field provides :attr:`~Field.choices`.
|
||||||
|
|
||||||
.. class:: NullBooleanSelect
|
.. class:: NullBooleanSelect
|
||||||
|
@ -123,22 +128,22 @@ commonly used groups of widgets:
|
||||||
.. class:: RadioSelect
|
.. class:: RadioSelect
|
||||||
|
|
||||||
A list of radio buttons:
|
A list of radio buttons:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><input type='radio' ...></li>
|
<li><input type='radio' ...></li>
|
||||||
...
|
...
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
Requires that your field provides :attr:`~Field.choices`.
|
Requires that your field provides :attr:`~Field.choices`.
|
||||||
|
|
||||||
.. class:: CheckboxSelectMultiple
|
.. class:: CheckboxSelectMultiple
|
||||||
|
|
||||||
A list of checkboxes:
|
A list of checkboxes:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><input type='checkbox' ...></li>
|
<li><input type='checkbox' ...></li>
|
||||||
...
|
...
|
||||||
|
@ -155,7 +160,7 @@ commonly used groups of widgets:
|
||||||
|
|
||||||
Takes two optional arguments, ``date_format`` and ``time_format``, which
|
Takes two optional arguments, ``date_format`` and ``time_format``, which
|
||||||
work just like the ``format`` argument for ``DateInput`` and ``TimeInput``.
|
work just like the ``format`` argument for ``DateInput`` and ``TimeInput``.
|
||||||
|
|
||||||
.. versionchanged:: 1.1
|
.. versionchanged:: 1.1
|
||||||
The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0.
|
The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0.
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,31 @@ fixes and an easy upgrade path from Django 1.2.
|
||||||
Backwards-incompatible changes in 1.3
|
Backwards-incompatible changes in 1.3
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
PasswordInput default rendering behavior
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Prior to Django 1.3, a :class:`~django.forms.PasswordInput` would render
|
||||||
|
data values like any other form. If a form submission raised an error,
|
||||||
|
the password that was submitted would be reflected to the client as form
|
||||||
|
data populating the form for resubmission.
|
||||||
|
|
||||||
|
This had the potential to leak passwords, as any failed password
|
||||||
|
attempt would cause the password that was typed to be sent back to the
|
||||||
|
client.
|
||||||
|
|
||||||
|
In Django 1.3, the default behavior of
|
||||||
|
:class:`~django.forms.PasswordInput` is to suppress the display of
|
||||||
|
password values. This change doesn't alter the way form data is
|
||||||
|
validated or handled. It only affects the user experience with
|
||||||
|
passwords on a form when they make an error submitting form data (such
|
||||||
|
as on unsuccessful logins, or when completing a registration form).
|
||||||
|
|
||||||
|
If you want restore the pre-Django 1.3 behavior, you need to pass in a
|
||||||
|
custom widget to your form that sets the ``render_value`` argument::
|
||||||
|
|
||||||
|
class LoginForm(forms.Form):
|
||||||
|
username = forms.CharField(max_length=100)
|
||||||
|
password = forms.PasswordField(widget=forms.PasswordInput(render_value=True))
|
||||||
|
|
||||||
|
|
||||||
Features deprecated in 1.3
|
Features deprecated in 1.3
|
||||||
|
|
|
@ -705,13 +705,13 @@ Form.clean() is required to return a dictionary of all clean data.
|
||||||
>>> print f.as_table()
|
>>> print f.as_table()
|
||||||
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
||||||
<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>
|
<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>
|
||||||
<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
|
<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr>
|
||||||
<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
|
<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
|
<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
|
||||||
<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
|
<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
|
||||||
<li>Password1: <input type="password" name="password1" value="foo" /></li>
|
<li>Password1: <input type="password" name="password1" /></li>
|
||||||
<li>Password2: <input type="password" name="password2" value="bar" /></li>
|
<li>Password2: <input type="password" name="password2" /></li>
|
||||||
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
|
@ -1589,8 +1589,8 @@ Case 2: POST with erroneous data (a redisplayed form, with errors).
|
||||||
<table>
|
<table>
|
||||||
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
||||||
<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
|
<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
|
||||||
<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
|
<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr>
|
||||||
<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
|
<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
|
||||||
</table>
|
</table>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
|
@ -1719,8 +1719,8 @@ the list of errors is empty). You can also use it in {% if %} statements.
|
||||||
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
|
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
|
||||||
<form action="">
|
<form action="">
|
||||||
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
|
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
|
||||||
<p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
|
<p><label>Password: <input type="password" name="password1" /></label></p>
|
||||||
<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
|
<p><label>Password (again): <input type="password" name="password2" /></label></p>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
>>> t = Template('''<form action="">
|
>>> t = Template('''<form action="">
|
||||||
|
@ -1734,8 +1734,8 @@ the list of errors is empty). You can also use it in {% if %} statements.
|
||||||
<form action="">
|
<form action="">
|
||||||
<ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
|
<ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
|
||||||
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
|
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
|
||||||
<p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
|
<p><label>Password: <input type="password" name="password1" /></label></p>
|
||||||
<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
|
<p><label>Password (again): <input type="password" name="password2" /></label></p>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,17 @@ u'<input type="text" class="special" name="email" />'
|
||||||
u'<input type="password" name="email" />'
|
u'<input type="password" name="email" />'
|
||||||
>>> w.render('email', None)
|
>>> w.render('email', None)
|
||||||
u'<input type="password" name="email" />'
|
u'<input type="password" name="email" />'
|
||||||
|
>>> w.render('email', 'secret')
|
||||||
|
u'<input type="password" name="email" />'
|
||||||
|
|
||||||
|
The render_value argument lets you specify whether the widget should render
|
||||||
|
its value. For security reasons, this is off by default.
|
||||||
|
|
||||||
|
>>> w = PasswordInput(render_value=True)
|
||||||
|
>>> w.render('email', '')
|
||||||
|
u'<input type="password" name="email" />'
|
||||||
|
>>> w.render('email', None)
|
||||||
|
u'<input type="password" name="email" />'
|
||||||
>>> w.render('email', 'test@example.com')
|
>>> w.render('email', 'test@example.com')
|
||||||
u'<input type="password" name="email" value="test@example.com" />'
|
u'<input type="password" name="email" value="test@example.com" />'
|
||||||
>>> w.render('email', 'some "quoted" & ampersanded value')
|
>>> w.render('email', 'some "quoted" & ampersanded value')
|
||||||
|
@ -70,36 +81,20 @@ u'<input type="password" name="email" value="some "quoted" & amper
|
||||||
u'<input type="password" name="email" value="test@example.com" class="fun" />'
|
u'<input type="password" name="email" value="test@example.com" class="fun" />'
|
||||||
|
|
||||||
You can also pass 'attrs' to the constructor:
|
You can also pass 'attrs' to the constructor:
|
||||||
>>> w = PasswordInput(attrs={'class': 'fun'})
|
>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=True)
|
||||||
>>> w.render('email', '')
|
>>> w.render('email', '')
|
||||||
u'<input type="password" class="fun" name="email" />'
|
u'<input type="password" class="fun" name="email" />'
|
||||||
>>> w.render('email', 'foo@example.com')
|
>>> w.render('email', 'foo@example.com')
|
||||||
u'<input type="password" class="fun" value="foo@example.com" name="email" />'
|
u'<input type="password" class="fun" value="foo@example.com" name="email" />'
|
||||||
|
|
||||||
'attrs' passed to render() get precedence over those passed to the constructor:
|
'attrs' passed to render() get precedence over those passed to the constructor:
|
||||||
>>> w = PasswordInput(attrs={'class': 'pretty'})
|
>>> w = PasswordInput(attrs={'class': 'pretty'}, render_value=True)
|
||||||
>>> w.render('email', '', attrs={'class': 'special'})
|
>>> w.render('email', '', attrs={'class': 'special'})
|
||||||
u'<input type="password" class="special" name="email" />'
|
u'<input type="password" class="special" name="email" />'
|
||||||
|
|
||||||
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
||||||
u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
|
u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
|
||||||
|
|
||||||
The render_value argument lets you specify whether the widget should render
|
|
||||||
its value. You may want to do this for security reasons.
|
|
||||||
>>> w = PasswordInput(render_value=True)
|
|
||||||
>>> w.render('email', 'secret')
|
|
||||||
u'<input type="password" name="email" value="secret" />'
|
|
||||||
>>> w = PasswordInput(render_value=False)
|
|
||||||
>>> w.render('email', '')
|
|
||||||
u'<input type="password" name="email" />'
|
|
||||||
>>> w.render('email', None)
|
|
||||||
u'<input type="password" name="email" />'
|
|
||||||
>>> w.render('email', 'secret')
|
|
||||||
u'<input type="password" name="email" />'
|
|
||||||
>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False)
|
|
||||||
>>> w.render('email', 'secret')
|
|
||||||
u'<input type="password" class="fun" name="email" />'
|
|
||||||
|
|
||||||
# HiddenInput Widget ############################################################
|
# HiddenInput Widget ############################################################
|
||||||
|
|
||||||
>>> w = HiddenInput()
|
>>> w = HiddenInput()
|
||||||
|
@ -1286,7 +1281,7 @@ class SelectAndTextWidget(forms.MultiWidget):
|
||||||
forms.TextInput
|
forms.TextInput
|
||||||
]
|
]
|
||||||
super(SelectAndTextWidget, self).__init__(widgets)
|
super(SelectAndTextWidget, self).__init__(widgets)
|
||||||
|
|
||||||
def _set_choices(self, choices):
|
def _set_choices(self, choices):
|
||||||
"""
|
"""
|
||||||
When choices are set for this widget, we want to pass those along to the Select widget
|
When choices are set for this widget, we want to pass those along to the Select widget
|
||||||
|
|
Loading…
Reference in New Issue