summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHasan Ramezani <hasan.r67@gmail.com>2019-03-10 11:03:42 +0100
committerCarlton Gibson <carlton@noumenal.es>2019-06-07 12:44:39 +0200
commitdcb8f00d06eec99072b78d54215c9a3dc04acb99 (patch)
treec5f55c29e3d55df56644ed6738809f1d0330e5e2
parentc498f088c584ec3aff97409fdc11b39b28240de9 (diff)
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>
-rw-r--r--django/contrib/auth/forms.py29
-rw-r--r--docs/releases/3.0.txt4
-rw-r--r--tests/auth_tests/test_forms.py51
3 files changed, 73 insertions, 11 deletions
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py
index ca7529ab5f..23d5bdafed 100644
--- a/django/contrib/auth/forms.py
+++ b/django/contrib/auth/forms.py
@@ -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."),
)
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 1ba81c5643..f6a45ebfc9 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -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`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py
index 19397ad5d6..f70a7f830d 100644
--- a/tests/auth_tests/test_forms.py
+++ b/tests/auth_tests/test_forms.py
@@ -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)