diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 0376d177094..eaae0bf6953 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -147,16 +147,22 @@ class BaseUserCreationForm(forms.ModelForm): class UserCreationForm(BaseUserCreationForm): - error_messages = { - **BaseUserCreationForm.error_messages, - "unique": _("A user with that username already exists."), - } - def clean_username(self): """Reject usernames that differ only in case.""" username = self.cleaned_data.get("username") - if username and User.objects.filter(username__iexact=username).exists(): - raise forms.ValidationError(self.error_messages["unique"], code="unique") + if ( + username + and self._meta.model.objects.filter(username__iexact=username).exists() + ): + self._update_errors( + ValidationError( + { + "username": self.instance.unique_error_message( + self._meta.model, ["username"] + ) + } + ) + ) else: return username diff --git a/django/contrib/auth/locale/en/LC_MESSAGES/django.po b/django/contrib/auth/locale/en/LC_MESSAGES/django.po index bc6eff91dd1..8b15915f9fa 100644 --- a/django/contrib/auth/locale/en/LC_MESSAGES/django.po +++ b/django/contrib/auth/locale/en/LC_MESSAGES/django.po @@ -77,10 +77,6 @@ msgstr "" msgid "Enter the same password as before, for verification." msgstr "" -#: contrib/auth/forms.py:152 contrib/auth/models.py:353 -msgid "A user with that username already exists." -msgstr "" - #: contrib/auth/forms.py:168 msgid "" "Raw passwords are not stored, so there is no way to see this user’s " @@ -240,6 +236,10 @@ msgstr "" msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "" +#: contrib/auth/models.py:353 +msgid "A user with that username already exists." +msgstr "" + #: contrib/auth/models.py:356 msgid "first name" msgstr "" diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py index c3ce1f570ff..7a80adbf314 100644 --- a/tests/auth_tests/test_forms.py +++ b/tests/auth_tests/test_forms.py @@ -372,6 +372,35 @@ class UserCreationFormTest(TestDataMixin, TestCase): ["A user with that username already exists."], ) + @override_settings(AUTH_USER_MODEL="auth_tests.ExtensionUser") + def test_case_insensitive_username_custom_user_and_error_message(self): + class CustomUserCreationForm(UserCreationForm): + class Meta(UserCreationForm.Meta): + model = ExtensionUser + fields = UserCreationForm.Meta.fields + ("date_of_birth",) + error_messages = { + "username": {"unique": "This username has already been taken."} + } + + ExtensionUser.objects.create_user( + username="testclient", + password="password", + email="testclient@example.com", + date_of_birth=datetime.date(1984, 3, 5), + ) + data = { + "username": "TeStClIeNt", + "password1": "test123", + "password2": "test123", + "date_of_birth": "1980-01-01", + } + form = CustomUserCreationForm(data) + self.assertIs(form.is_valid(), False) + self.assertEqual( + form["username"].errors, + ["This username has already been taken."], + ) + # To verify that the login form rejects inactive users, use an authentication # backend that allows them.