diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index 201f28ef37..9c9aa21f57 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -57,6 +57,7 @@ class AdminSite: app_index_template = None login_template = None logout_template = None + password_change_form = None password_change_template = None password_change_done_template = None @@ -355,7 +356,7 @@ class AdminSite: url = reverse("admin:password_change_done", current_app=self.name) defaults = { - "form_class": AdminPasswordChangeForm, + "form_class": self.password_change_form or AdminPasswordChangeForm, "success_url": url, "extra_context": {**self.each_context(request), **(extra_context or {})}, } diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 311b21ec6e..9fc5744dbd 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -2980,6 +2980,13 @@ Templates can override or extend base admin templates as described in Path to a custom template that will be used by the admin site logout view. +.. attribute:: AdminSite.password_change_form + + .. versionadded:: 6.0 + + Subclass of :class:`~django.contrib.auth.forms.PasswordChangeForm` that + will be used by the admin site password change view. + .. attribute:: AdminSite.password_change_template Path to a custom template that will be used by the admin site password diff --git a/docs/releases/6.0.txt b/docs/releases/6.0.txt index 0645441c61..03b1c52c1d 100644 --- a/docs/releases/6.0.txt +++ b/docs/releases/6.0.txt @@ -48,7 +48,8 @@ Minor features :mod:`django.contrib.admindocs` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ... +* The new :attr:`.AdminSite.password_change_form` attribute allows customizing + the form used in the admin site password change view. :mod:`django.contrib.auth` ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/admin_views/customadmin.py b/tests/admin_views/customadmin.py index 4b30c5c30f..a63d24a9ee 100644 --- a/tests/admin_views/customadmin.py +++ b/tests/admin_views/customadmin.py @@ -18,6 +18,7 @@ class Admin2(admin.AdminSite): login_template = "custom_admin/login.html" logout_template = "custom_admin/logout.html" index_template = ["custom_admin/index.html"] # a list, to test fix for #18697 + password_change_form = forms.CustomAdminPasswordChangeForm password_change_template = "custom_admin/password_change_form.html" password_change_done_template = "custom_admin/password_change_done.html" diff --git a/tests/admin_views/forms.py b/tests/admin_views/forms.py index e9d9c0a8a5..3a3566c10f 100644 --- a/tests/admin_views/forms.py +++ b/tests/admin_views/forms.py @@ -1,4 +1,4 @@ -from django.contrib.admin.forms import AdminAuthenticationForm +from django.contrib.admin.forms import AdminAuthenticationForm, AdminPasswordChangeForm from django.contrib.admin.helpers import ActionForm from django.core.exceptions import ValidationError @@ -14,6 +14,12 @@ class CustomAdminAuthenticationForm(AdminAuthenticationForm): return username +class CustomAdminPasswordChangeForm(AdminPasswordChangeForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields["old_password"].label = "Custom old password label" + + class MediaActionForm(ActionForm): class Media: js = ["path/to/media.js"] diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 7642a186c0..ec6fd58d53 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -1820,6 +1820,11 @@ class AdminCustomTemplateTests(AdminViewBasicTestCase): response = user_admin.user_change_password(request, str(user.pk)) self.assertContains(response, '
') + def test_custom_password_change_form(self): + self.client.force_login(self.superuser) + response = self.client.get(reverse("admin4:password_change")) + self.assertContains(response, "Custom old password label") + def test_extended_bodyclass_template_index(self): """ The admin/index.html template uses block.super in the bodyclass block.