summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/admin/widgets.py4
-rw-r--r--docs/releases/6.0.4.txt4
-rw-r--r--tests/admin_views/tests.py13
-rw-r--r--tests/modeladmin/tests.py49
4 files changed, 68 insertions, 2 deletions
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index f5c3939012..1dfac1b323 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -28,6 +28,8 @@ class FilteredSelectMultiple(forms.SelectMultiple):
catalog has been loaded in the page
"""
+ use_fieldset = True
+
class Media:
js = [
"admin/js/core.js",
@@ -300,7 +302,7 @@ class RelatedFieldWidgetWrapper(forms.Widget):
self.can_view_related = supported and can_view_related
# To check if the related object is registered with this AdminSite.
self.admin_site = admin_site
- self.use_fieldset = True
+ self.use_fieldset = widget.use_fieldset
def __deepcopy__(self, memo):
obj = copy.copy(self)
diff --git a/docs/releases/6.0.4.txt b/docs/releases/6.0.4.txt
index e24397d9bc..1967edacc8 100644
--- a/docs/releases/6.0.4.txt
+++ b/docs/releases/6.0.4.txt
@@ -14,3 +14,7 @@ Bugfixes
and :func:`~django.contrib.auth.alogout` did not respectively set or clear
``request.user`` if it had already been materialized (e.g., by sync
middleware) (:ticket:`37017`).
+
+* Fixed a regression in Django 6.0 in admin forms where
+ ``RelatedFieldWidgetWrapper`` incorrectly wrapped all widgets in a
+ ``<fieldset>`` (:ticket:`36949`).
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 203e1d25a9..f766794c54 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -6922,17 +6922,28 @@ class SeleniumTests(AdminSeleniumTestCase):
"Difficulty:",
"Materials:",
"Start datetime:",
- "Categories:",
]
url = reverse("admin:admin_views_course_change", args=(course.pk,))
self.selenium.get(self.live_server_url + url)
fieldsets = self.selenium.find_elements(
By.CSS_SELECTOR, "fieldset.aligned fieldset"
)
+ self.assertEqual(len(fieldsets), len(expected_legend_tags_text))
for index, fieldset in enumerate(fieldsets):
legend = fieldset.find_element(By.TAG_NAME, "legend")
self.assertEqual(legend.text, expected_legend_tags_text[index])
+ # FilteredSelectMultiple uses <fieldset>.
+ url = reverse("admin:admin_views_camelcaserelatedmodel_add")
+ self.selenium.get(self.live_server_url + url)
+ fieldsets = self.selenium.find_elements(
+ By.CSS_SELECTOR, "fieldset.aligned fieldset"
+ )
+ self.assertEqual(len(fieldsets), 1)
+ for index, fieldset in enumerate(fieldsets):
+ legend = fieldset.find_element(By.TAG_NAME, "legend")
+ self.assertEqual(legend.text, "M2m:")
+
@screenshot_cases(["desktop_size", "mobile_size", "rtl", "dark", "high_contrast"])
def test_use_fieldset_with_grouped_fields(self):
from selenium.webdriver.common.by import By
diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py
index 4a92514e17..c1c8b52346 100644
--- a/tests/modeladmin/tests.py
+++ b/tests/modeladmin/tests.py
@@ -830,6 +830,55 @@ class ModelAdminTests(TestCase):
["extra", "transport", "id", "DELETE", "main_band"],
)
+ def test_foreign_key_as_custom_widget(self):
+ class CustomSelectMultiple(forms.SelectMultiple):
+ def build_attrs(self, base_attrs, extra_attrs=None):
+ attrs = super().build_attrs(base_attrs, extra_attrs)
+ attrs["data-custom-widget"] = "true"
+ return attrs
+
+ class ConcertAdmin(ModelAdmin):
+ formfield_overrides = {
+ models.ForeignKey: {"widget": CustomSelectMultiple},
+ }
+
+ cma = ConcertAdmin(Concert, self.site)
+ cmafa = cma.get_form(request)
+ expected = (
+ '<div><label for="id_main_band">Main band:</label><div '
+ 'class="related-widget-wrapper" data-model-ref="band"><select '
+ 'name="main_band" data-context="available-source" required '
+ 'id="id_main_band" data-custom-widget="true" multiple>'
+ '<option value="">---------</option><option value="1">The Doors</option>'
+ "</select></div></div>"
+ )
+ self.assertInHTML(expected, cmafa().render())
+
+ def test_foreign_key_as_custom_widget_with_fieldset(self):
+ class CustomSelectMultipleFieldset(forms.RadioSelect):
+ use_fieldset = True
+
+ def build_attrs(self, base_attrs, extra_attrs=None):
+ attrs = super().build_attrs(base_attrs, extra_attrs)
+ attrs["use_fieldset"] = "true"
+ return attrs
+
+ class ConcertAdmin(ModelAdmin):
+ formfield_overrides = {
+ models.ForeignKey: {"widget": CustomSelectMultipleFieldset},
+ }
+
+ cma = ConcertAdmin(Concert, self.site)
+ cmafa = cma.get_form(request)
+ expected = (
+ '<fieldset><legend>Main band:</legend><div class="related-widget-wrapper" '
+ 'data-model-ref="band"><div id="id_main_band"><div><label '
+ 'for="id_main_band_0"><input type="radio" name="main_band" value="1" '
+ 'data-context="available-source" required id="id_main_band_0" '
+ 'use_fieldset="true">The Doors</label></div></div></div></fieldset>'
+ )
+ self.assertInHTML(expected, cmafa().render())
+
def test_log_actions(self):
ma = ModelAdmin(Band, self.site)
mock_request = MockRequest()