Skip to content

Commit ea8e293

Browse files
smithdc1jacobtylerwalls
authored andcommitted
[6.0.x] Fixed #36949 -- Improved RelatedFieldWidgetWrapper <label>s.
Regression in 4187da2. Backport of 253f552 from main.
1 parent 1f0abb0 commit ea8e293

4 files changed

Lines changed: 68 additions & 2 deletions

File tree

django/contrib/admin/widgets.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class FilteredSelectMultiple(forms.SelectMultiple):
2828
catalog has been loaded in the page
2929
"""
3030

31+
use_fieldset = True
32+
3133
class Media:
3234
js = [
3335
"admin/js/core.js",
@@ -300,7 +302,7 @@ def __init__(
300302
self.can_view_related = supported and can_view_related
301303
# To check if the related object is registered with this AdminSite.
302304
self.admin_site = admin_site
303-
self.use_fieldset = True
305+
self.use_fieldset = widget.use_fieldset
304306

305307
def __deepcopy__(self, memo):
306308
obj = copy.copy(self)

docs/releases/6.0.4.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ Bugfixes
1414
and :func:`~django.contrib.auth.alogout` did not respectively set or clear
1515
``request.user`` if it had already been materialized (e.g., by sync
1616
middleware) (:ticket:`37017`).
17+
18+
* Fixed a regression in Django 6.0 in admin forms where
19+
``RelatedFieldWidgetWrapper`` incorrectly wrapped all widgets in a
20+
``<fieldset>`` (:ticket:`36949`).

tests/admin_views/tests.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6922,17 +6922,28 @@ def test_use_fieldset_fields_render(self):
69226922
"Difficulty:",
69236923
"Materials:",
69246924
"Start datetime:",
6925-
"Categories:",
69266925
]
69276926
url = reverse("admin:admin_views_course_change", args=(course.pk,))
69286927
self.selenium.get(self.live_server_url + url)
69296928
fieldsets = self.selenium.find_elements(
69306929
By.CSS_SELECTOR, "fieldset.aligned fieldset"
69316930
)
6931+
self.assertEqual(len(fieldsets), len(expected_legend_tags_text))
69326932
for index, fieldset in enumerate(fieldsets):
69336933
legend = fieldset.find_element(By.TAG_NAME, "legend")
69346934
self.assertEqual(legend.text, expected_legend_tags_text[index])
69356935

6936+
# FilteredSelectMultiple uses <fieldset>.
6937+
url = reverse("admin:admin_views_camelcaserelatedmodel_add")
6938+
self.selenium.get(self.live_server_url + url)
6939+
fieldsets = self.selenium.find_elements(
6940+
By.CSS_SELECTOR, "fieldset.aligned fieldset"
6941+
)
6942+
self.assertEqual(len(fieldsets), 1)
6943+
for index, fieldset in enumerate(fieldsets):
6944+
legend = fieldset.find_element(By.TAG_NAME, "legend")
6945+
self.assertEqual(legend.text, "M2m:")
6946+
69366947
@screenshot_cases(["desktop_size", "mobile_size", "rtl", "dark", "high_contrast"])
69376948
def test_use_fieldset_with_grouped_fields(self):
69386949
from selenium.webdriver.common.by import By

tests/modeladmin/tests.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,55 @@ class BandAdmin(ModelAdmin):
830830
["extra", "transport", "id", "DELETE", "main_band"],
831831
)
832832

833+
def test_foreign_key_as_custom_widget(self):
834+
class CustomSelectMultiple(forms.SelectMultiple):
835+
def build_attrs(self, base_attrs, extra_attrs=None):
836+
attrs = super().build_attrs(base_attrs, extra_attrs)
837+
attrs["data-custom-widget"] = "true"
838+
return attrs
839+
840+
class ConcertAdmin(ModelAdmin):
841+
formfield_overrides = {
842+
models.ForeignKey: {"widget": CustomSelectMultiple},
843+
}
844+
845+
cma = ConcertAdmin(Concert, self.site)
846+
cmafa = cma.get_form(request)
847+
expected = (
848+
'<div><label for="id_main_band">Main band:</label><div '
849+
'class="related-widget-wrapper" data-model-ref="band"><select '
850+
'name="main_band" data-context="available-source" required '
851+
'id="id_main_band" data-custom-widget="true" multiple>'
852+
'<option value="">---------</option><option value="1">The Doors</option>'
853+
"</select></div></div>"
854+
)
855+
self.assertInHTML(expected, cmafa().render())
856+
857+
def test_foreign_key_as_custom_widget_with_fieldset(self):
858+
class CustomSelectMultipleFieldset(forms.RadioSelect):
859+
use_fieldset = True
860+
861+
def build_attrs(self, base_attrs, extra_attrs=None):
862+
attrs = super().build_attrs(base_attrs, extra_attrs)
863+
attrs["use_fieldset"] = "true"
864+
return attrs
865+
866+
class ConcertAdmin(ModelAdmin):
867+
formfield_overrides = {
868+
models.ForeignKey: {"widget": CustomSelectMultipleFieldset},
869+
}
870+
871+
cma = ConcertAdmin(Concert, self.site)
872+
cmafa = cma.get_form(request)
873+
expected = (
874+
'<fieldset><legend>Main band:</legend><div class="related-widget-wrapper" '
875+
'data-model-ref="band"><div id="id_main_band"><div><label '
876+
'for="id_main_band_0"><input type="radio" name="main_band" value="1" '
877+
'data-context="available-source" required id="id_main_band_0" '
878+
'use_fieldset="true">The Doors</label></div></div></div></fieldset>'
879+
)
880+
self.assertInHTML(expected, cmafa().render())
881+
833882
def test_log_actions(self):
834883
ma = ModelAdmin(Band, self.site)
835884
mock_request = MockRequest()

0 commit comments

Comments
 (0)