From 12b9ef38b3ff7f5b8b24a5f42e8923fdb6db44bb Mon Sep 17 00:00:00 2001
From: Mohammadreza Eskandari
 <33634829+mohammadrezaesk@users.noreply.github.com>
Date: Tue, 21 Jan 2025 12:34:35 +0300
Subject: [PATCH] Fixed #36121 -- Allowed customizing the admin site password
 change form.

---
 django/contrib/admin/sites.py    | 3 ++-
 docs/ref/contrib/admin/index.txt | 7 +++++++
 docs/releases/6.0.txt            | 3 ++-
 tests/admin_views/customadmin.py | 1 +
 tests/admin_views/forms.py       | 8 +++++++-
 tests/admin_views/tests.py       | 5 +++++
 6 files changed, 24 insertions(+), 3 deletions(-)

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, '<div class="help">')
 
+    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.