Skip to content

Commit 53f3f6f

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents e9e28a5 + 339977d commit 53f3f6f

File tree

4 files changed

+73
-4
lines changed

4 files changed

+73
-4
lines changed

django/contrib/auth/forms.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,14 @@ def validate_passwords(
154154
if not usable_password:
155155
return self.cleaned_data
156156

157-
if not password1:
157+
if not password1 and password1_field_name not in self.errors:
158158
error = ValidationError(
159159
self.fields[password1_field_name].error_messages["required"],
160160
code="required",
161161
)
162162
self.add_error(password1_field_name, error)
163163

164-
if not password2:
164+
if not password2 and password2_field_name not in self.errors:
165165
error = ValidationError(
166166
self.fields[password2_field_name].error_messages["required"],
167167
code="required",

docs/ref/forms/validation.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ example::
370370
# Only do something if both fields are valid so far.
371371
if "help" not in subject:
372372
raise ValidationError(
373-
"Did not send for 'help' in the subject despite " "CC'ing yourself."
373+
"Did not send for 'help' in the subject despite CC'ing yourself."
374374
)
375375

376376
In this code, if the validation error is raised, the form will display an

docs/ref/models/instances.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ Then, ``full_clean()`` will check unique constraints on your model.
380380
raise ValidationError(
381381
{
382382
"status": _(
383-
"Set status to draft if there is not a " "publication date."
383+
"Set status to draft if there is not a publication date."
384384
),
385385
}
386386
)

tests/auth_tests/test_forms.py

+69
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ def setUpTestData(cls):
6060
)
6161

6262

63+
class ExtraValidationFormMixin:
64+
def __init__(self, *args, failing_fields=None, **kwargs):
65+
super().__init__(*args, **kwargs)
66+
self.failing_fields = failing_fields or {}
67+
68+
def failing_helper(self, field_name):
69+
if field_name in self.failing_fields:
70+
errors = [
71+
ValidationError(error, code="invalid")
72+
for error in self.failing_fields[field_name]
73+
]
74+
raise ValidationError(errors)
75+
return self.cleaned_data[field_name]
76+
77+
6378
class BaseUserCreationFormTest(TestDataMixin, TestCase):
6479
def test_user_already_exists(self):
6580
data = {
@@ -324,6 +339,22 @@ def test_password_help_text(self):
324339
"</li></ul>",
325340
)
326341

342+
def test_password_extra_validations(self):
343+
class ExtraValidationForm(ExtraValidationFormMixin, BaseUserCreationForm):
344+
def clean_password1(self):
345+
return self.failing_helper("password1")
346+
347+
def clean_password2(self):
348+
return self.failing_helper("password2")
349+
350+
data = {"username": "extra", "password1": "abc", "password2": "abc"}
351+
for fields in (["password1"], ["password2"], ["password1", "password2"]):
352+
with self.subTest(fields=fields):
353+
errors = {field: [f"Extra validation for {field}."] for field in fields}
354+
form = ExtraValidationForm(data, failing_fields=errors)
355+
self.assertIs(form.is_valid(), False)
356+
self.assertDictEqual(form.errors, errors)
357+
327358
@override_settings(
328359
AUTH_PASSWORD_VALIDATORS=[
329360
{
@@ -865,6 +896,27 @@ def test_html_autocomplete_attributes(self):
865896
form.fields[field_name].widget.attrs["autocomplete"], autocomplete
866897
)
867898

899+
def test_password_extra_validations(self):
900+
class ExtraValidationForm(ExtraValidationFormMixin, SetPasswordForm):
901+
def clean_new_password1(self):
902+
return self.failing_helper("new_password1")
903+
904+
def clean_new_password2(self):
905+
return self.failing_helper("new_password2")
906+
907+
user = User.objects.get(username="testclient")
908+
data = {"new_password1": "abc", "new_password2": "abc"}
909+
for fields in (
910+
["new_password1"],
911+
["new_password2"],
912+
["new_password1", "new_password2"],
913+
):
914+
with self.subTest(fields=fields):
915+
errors = {field: [f"Extra validation for {field}."] for field in fields}
916+
form = ExtraValidationForm(user, data, failing_fields=errors)
917+
self.assertIs(form.is_valid(), False)
918+
self.assertDictEqual(form.errors, errors)
919+
868920

869921
class PasswordChangeFormTest(TestDataMixin, TestCase):
870922
def test_incorrect_password(self):
@@ -1456,6 +1508,23 @@ def test_password_whitespace_not_stripped(self):
14561508
self.assertEqual(form.cleaned_data["password2"], data["password2"])
14571509
self.assertEqual(form.changed_data, ["password"])
14581510

1511+
def test_password_extra_validations(self):
1512+
class ExtraValidationForm(ExtraValidationFormMixin, AdminPasswordChangeForm):
1513+
def clean_password1(self):
1514+
return self.failing_helper("password1")
1515+
1516+
def clean_password2(self):
1517+
return self.failing_helper("password2")
1518+
1519+
user = User.objects.get(username="testclient")
1520+
data = {"username": "extra", "password1": "abc", "password2": "abc"}
1521+
for fields in (["password1"], ["password2"], ["password1", "password2"]):
1522+
with self.subTest(fields=fields):
1523+
errors = {field: [f"Extra validation for {field}."] for field in fields}
1524+
form = ExtraValidationForm(user, data, failing_fields=errors)
1525+
self.assertIs(form.is_valid(), False)
1526+
self.assertDictEqual(form.errors, errors)
1527+
14591528
def test_non_matching_passwords(self):
14601529
user = User.objects.get(username="testclient")
14611530
data = {"password1": "password1", "password2": "password2"}

0 commit comments

Comments
 (0)