Fixed #29379 -- Added autocomplete attribute to contrib.auth.forms fields.
Thank you to Nick Pope for review. Co-authored-by: CHI Cheng <cloudream@gmail.com>
This commit is contained in:
parent
c498f088c5
commit
dcb8f00d06
|
@ -78,12 +78,12 @@ class UserCreationForm(forms.ModelForm):
|
|||
password1 = forms.CharField(
|
||||
label=_("Password"),
|
||||
strip=False,
|
||||
widget=forms.PasswordInput,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
|
||||
help_text=password_validation.password_validators_help_text_html(),
|
||||
)
|
||||
password2 = forms.CharField(
|
||||
label=_("Password confirmation"),
|
||||
widget=forms.PasswordInput,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
|
||||
strip=False,
|
||||
help_text=_("Enter the same password as before, for verification."),
|
||||
)
|
||||
|
@ -96,7 +96,10 @@ class UserCreationForm(forms.ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self._meta.model.USERNAME_FIELD in self.fields:
|
||||
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({'autofocus': True})
|
||||
self.fields[self._meta.model.USERNAME_FIELD].widget.attrs.update({
|
||||
'autocomplete': 'username',
|
||||
'autofocus': True,
|
||||
})
|
||||
|
||||
def clean_password2(self):
|
||||
password1 = self.cleaned_data.get("password1")
|
||||
|
@ -163,11 +166,11 @@ class AuthenticationForm(forms.Form):
|
|||
Base class for authenticating users. Extend this to get a form that accepts
|
||||
username/password logins.
|
||||
"""
|
||||
username = UsernameField(widget=forms.TextInput(attrs={'autofocus': True}))
|
||||
username = UsernameField(widget=forms.TextInput(attrs={'autocomplete': 'username', 'autofocus': True}))
|
||||
password = forms.CharField(
|
||||
label=_("Password"),
|
||||
strip=False,
|
||||
widget=forms.PasswordInput,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'current-password'}),
|
||||
)
|
||||
|
||||
error_messages = {
|
||||
|
@ -235,7 +238,11 @@ class AuthenticationForm(forms.Form):
|
|||
|
||||
|
||||
class PasswordResetForm(forms.Form):
|
||||
email = forms.EmailField(label=_("Email"), max_length=254)
|
||||
email = forms.EmailField(
|
||||
label=_("Email"),
|
||||
max_length=254,
|
||||
widget=forms.EmailInput(attrs={'autocomplete': 'email'})
|
||||
)
|
||||
|
||||
def send_mail(self, subject_template_name, email_template_name,
|
||||
context, from_email, to_email, html_email_template_name=None):
|
||||
|
@ -311,14 +318,14 @@ class SetPasswordForm(forms.Form):
|
|||
}
|
||||
new_password1 = forms.CharField(
|
||||
label=_("New password"),
|
||||
widget=forms.PasswordInput,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
|
||||
strip=False,
|
||||
help_text=password_validation.password_validators_help_text_html(),
|
||||
)
|
||||
new_password2 = forms.CharField(
|
||||
label=_("New password confirmation"),
|
||||
strip=False,
|
||||
widget=forms.PasswordInput,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
|
||||
)
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
|
@ -357,7 +364,7 @@ class PasswordChangeForm(SetPasswordForm):
|
|||
old_password = forms.CharField(
|
||||
label=_("Old password"),
|
||||
strip=False,
|
||||
widget=forms.PasswordInput(attrs={'autofocus': True}),
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'current-password', 'autofocus': True}),
|
||||
)
|
||||
|
||||
field_order = ['old_password', 'new_password1', 'new_password2']
|
||||
|
@ -385,13 +392,13 @@ class AdminPasswordChangeForm(forms.Form):
|
|||
required_css_class = 'required'
|
||||
password1 = forms.CharField(
|
||||
label=_("Password"),
|
||||
widget=forms.PasswordInput(attrs={'autofocus': True}),
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password', 'autofocus': True}),
|
||||
strip=False,
|
||||
help_text=password_validation.password_validators_help_text_html(),
|
||||
)
|
||||
password2 = forms.CharField(
|
||||
label=_("Password (again)"),
|
||||
widget=forms.PasswordInput,
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
|
||||
strip=False,
|
||||
help_text=_("Enter the same password as before, for verification."),
|
||||
)
|
||||
|
|
|
@ -76,6 +76,10 @@ Minor features
|
|||
to mirror the existing
|
||||
:meth:`~django.contrib.auth.models.User.get_group_permissions()` method.
|
||||
|
||||
* Added HTML ``autocomplete`` attribute to widgets of username, email, and
|
||||
password fields in :mod:`django.contrib.auth.forms` for better interaction
|
||||
with browser password managers.
|
||||
|
||||
:mod:`django.contrib.contenttypes`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -265,6 +265,17 @@ class UserCreationFormTest(TestDataMixin, TestCase):
|
|||
form = UserCreationForm()
|
||||
self.assertEqual(form.fields['username'].widget.attrs.get('autocapitalize'), 'none')
|
||||
|
||||
def test_html_autocomplete_attributes(self):
|
||||
form = UserCreationForm()
|
||||
tests = (
|
||||
('username', 'username'),
|
||||
('password1', 'new-password'),
|
||||
('password2', 'new-password'),
|
||||
)
|
||||
for field_name, autocomplete in tests:
|
||||
with self.subTest(field_name=field_name, autocomplete=autocomplete):
|
||||
self.assertEqual(form.fields[field_name].widget.attrs['autocomplete'], autocomplete)
|
||||
|
||||
|
||||
# To verify that the login form rejects inactive users, use an authentication
|
||||
# backend that allows them.
|
||||
|
@ -492,6 +503,16 @@ class AuthenticationFormTest(TestDataMixin, TestCase):
|
|||
self.assertEqual(error.code, 'invalid_login')
|
||||
self.assertEqual(error.params, {'username': 'username'})
|
||||
|
||||
def test_html_autocomplete_attributes(self):
|
||||
form = AuthenticationForm()
|
||||
tests = (
|
||||
('username', 'username'),
|
||||
('password', 'current-password'),
|
||||
)
|
||||
for field_name, autocomplete in tests:
|
||||
with self.subTest(field_name=field_name, autocomplete=autocomplete):
|
||||
self.assertEqual(form.fields[field_name].widget.attrs['autocomplete'], autocomplete)
|
||||
|
||||
|
||||
class SetPasswordFormTest(TestDataMixin, TestCase):
|
||||
|
||||
|
@ -572,6 +593,16 @@ class SetPasswordFormTest(TestDataMixin, TestCase):
|
|||
for french_text in french_help_texts:
|
||||
self.assertIn(french_text, html)
|
||||
|
||||
def test_html_autocomplete_attributes(self):
|
||||
form = SetPasswordForm(self.u1)
|
||||
tests = (
|
||||
('new_password1', 'new-password'),
|
||||
('new_password2', 'new-password'),
|
||||
)
|
||||
for field_name, autocomplete in tests:
|
||||
with self.subTest(field_name=field_name, autocomplete=autocomplete):
|
||||
self.assertEqual(form.fields[field_name].widget.attrs['autocomplete'], autocomplete)
|
||||
|
||||
|
||||
class PasswordChangeFormTest(TestDataMixin, TestCase):
|
||||
|
||||
|
@ -633,6 +664,11 @@ class PasswordChangeFormTest(TestDataMixin, TestCase):
|
|||
self.assertEqual(form.cleaned_data['new_password1'], data['new_password1'])
|
||||
self.assertEqual(form.cleaned_data['new_password2'], data['new_password2'])
|
||||
|
||||
def test_html_autocomplete_attributes(self):
|
||||
user = User.objects.get(username='testclient')
|
||||
form = PasswordChangeForm(user)
|
||||
self.assertEqual(form.fields['old_password'].widget.attrs['autocomplete'], 'current-password')
|
||||
|
||||
|
||||
class UserChangeFormTest(TestDataMixin, TestCase):
|
||||
|
||||
|
@ -916,6 +952,10 @@ class PasswordResetFormTest(TestDataMixin, TestCase):
|
|||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].to, [email])
|
||||
|
||||
def test_html_autocomplete_attributes(self):
|
||||
form = PasswordResetForm()
|
||||
self.assertEqual(form.fields['email'].widget.attrs['autocomplete'], 'email')
|
||||
|
||||
|
||||
class ReadOnlyPasswordHashTest(SimpleTestCase):
|
||||
|
||||
|
@ -997,3 +1037,14 @@ class AdminPasswordChangeFormTest(TestDataMixin, TestCase):
|
|||
form2 = AdminPasswordChangeForm(user, {'password1': 'test', 'password2': ''})
|
||||
self.assertEqual(form2.errors['password2'], required_error)
|
||||
self.assertNotIn('password1', form2.errors)
|
||||
|
||||
def test_html_autocomplete_attributes(self):
|
||||
user = User.objects.get(username='testclient')
|
||||
form = AdminPasswordChangeForm(user)
|
||||
tests = (
|
||||
('password1', 'new-password'),
|
||||
('password2', 'new-password'),
|
||||
)
|
||||
for field_name, autocomplete in tests:
|
||||
with self.subTest(field_name=field_name, autocomplete=autocomplete):
|
||||
self.assertEqual(form.fields[field_name].widget.attrs['autocomplete'], autocomplete)
|
||||
|
|
Loading…
Reference in New Issue