From 7770665a0bc7bba5d5f7f410b17651dccbb267e5 Mon Sep 17 00:00:00 2001 From: saJaeHyukc Date: Mon, 2 Dec 2024 19:31:02 +0900 Subject: [PATCH] Fixed #34624 - Removed edit and view options in RelatedFieldWidgetWrapper for radioselects. Signed-off-by: saJaeHyukc --- django/contrib/admin/tests.py | 1 + django/contrib/admin/widgets.py | 12 +++--- tests/admin_views/admin.py | 8 +++- .../test_related_object_lookups.py | 38 +++++++++++++++++++ tests/runtests.py | 1 + 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py index 3810c359e5..b4e392bcef 100644 --- a/django/contrib/admin/tests.py +++ b/django/contrib/admin/tests.py @@ -26,6 +26,7 @@ class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", + "django.contrib.redirects", ] def wait_until(self, callback, timeout=10): diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 74004a7ba7..0ea27a4a1b 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -270,15 +270,17 @@ class RelatedFieldWidgetWrapper(forms.Widget): if can_add_related is None: can_add_related = admin_site.is_registered(rel.model) self.can_add_related = can_add_related - # XXX: The UX does not support multiple selected values. - multiple = getattr(widget, "allow_multiple_selected", False) if not isinstance(widget, AutocompleteMixin): self.attrs["data-context"] = "available-source" - self.can_change_related = not multiple and can_change_related + # Widgets that display multiple values are not supported. + supported = not getattr( + widget, "allow_multiple_selected", False + ) and not isinstance(widget, AdminRadioSelect) + self.can_change_related = supported and can_change_related # XXX: The deletion UX can be confusing when dealing with cascading deletion. cascade = getattr(rel, "on_delete", None) is CASCADE - self.can_delete_related = not multiple and not cascade and can_delete_related - self.can_view_related = not multiple and can_view_related + self.can_delete_related = supported and not cascade and can_delete_related + self.can_view_related = supported and can_view_related # so we can check if the related object is registered with this AdminSite self.admin_site = admin_site diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index 5e14069bae..1bfea3914b 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -8,6 +8,10 @@ from django.contrib.admin import BooleanFieldListFilter from django.contrib.admin.views.main import ChangeList from django.contrib.auth.admin import GroupAdmin, UserAdmin from django.contrib.auth.models import Group, User +from django.contrib.redirects.admin import RedirectAdmin +from django.contrib.redirects.models import Redirect +from django.contrib.sites.admin import SiteAdmin +from django.contrib.sites.models import Site from django.core.exceptions import ValidationError from django.core.mail import EmailMessage from django.db import models @@ -1315,9 +1319,11 @@ site.register(Square, SquareAdmin) site.register(CamelCaseModel) site.register(CamelCaseRelatedModel, CamelCaseAdmin) -# Register core models we need in our tests +# Register core and contrib models we need in our tests site.register(User, UserAdmin) site.register(Group, GroupAdmin) +site.register(Redirect, RedirectAdmin) +site.register(Site, SiteAdmin) # Used to test URL namespaces site2 = admin.AdminSite(name="namespaced_admin") diff --git a/tests/admin_views/test_related_object_lookups.py b/tests/admin_views/test_related_object_lookups.py index 968558491c..0ef5705f26 100644 --- a/tests/admin_views/test_related_object_lookups.py +++ b/tests/admin_views/test_related_object_lookups.py @@ -179,3 +179,41 @@ class SeleniumTests(AdminSeleniumTestCase): m2m_to.get_attribute("innerHTML"), f"""""", ) + + def test_radio_select_widget_no_edit_view_icons(self): + from selenium.webdriver.common.by import By + + # Navigate to the Redirect add page in the admin + redirect_add_url = reverse("admin:redirects_redirect_add") + self.selenium.get(self.live_server_url + redirect_add_url) + + # Wait until the 'site' field is present + self.wait_until( + lambda driver: driver.find_element(By.CSS_SELECTOR, ".form-row.field-site") + ) + + # Find the 'site' field container + field_container = self.selenium.find_element( + By.CSS_SELECTOR, ".form-row.field-site" + ) + + # Find the widget wrapper + widget_wrapper = field_container.find_element( + By.CSS_SELECTOR, ".related-widget-wrapper" + ) + + # Check for edit and view icons + edit_icons = widget_wrapper.find_elements(By.CSS_SELECTOR, ".change-related") + view_icons = widget_wrapper.find_elements(By.CSS_SELECTOR, ".view-related") + + # Assert that no edit or view icons are present + self.assertEqual( + len(edit_icons), + 0, + "Edit icon should not be present for RadioSelect fields.", + ) + self.assertEqual( + len(view_icons), + 0, + "View icon should not be present for RadioSelect fields.", + ) diff --git a/tests/runtests.py b/tests/runtests.py index 516da84768..5fba8209f5 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -92,6 +92,7 @@ ALWAYS_INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.admin.apps.SimpleAdminConfig", "django.contrib.staticfiles", + "django.contrib.redirects", ] ALWAYS_MIDDLEWARE = [