summaryrefslogtreecommitdiff
path: root/tests/admin_views/tests.py
diff options
context:
space:
mode:
authorseanhelvey <sean.helvey@gmail.com>2024-12-13 11:56:53 -0800
committerJacob Walls <jacobtylerwalls@gmail.com>2026-01-22 21:12:23 -0500
commitb1ffa9a9d78b0c2c5ad6ed5a1d84e380d5cfd010 (patch)
tree0fcfd9b90c788e21e58cb9249f4119062b8bfc4e /tests/admin_views/tests.py
parent3851601b2e080df34fb9227fe5d2fd43af604263 (diff)
Fixed #13883 -- Rendered named choice groups with <optgroup> in FilteredSelectMultiple.
This patch adds support for <optgroup>s in FilteredSelectMultiple widgets. When a popup returns a new object, if the source field contains optgroup choices, the optgroup is now also included in the response data. Additionally, this adds error handling for invalid source_model parameters to prevent crashes and display user-friendly error messages instead. Co-authored-by: Michael McLarnon <mmclar@gmail.com>
Diffstat (limited to 'tests/admin_views/tests.py')
-rw-r--r--tests/admin_views/tests.py122
1 files changed, 121 insertions, 1 deletions
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index f7eaad659e..3377a6d441 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -11,7 +11,7 @@ from django.contrib import admin
from django.contrib.admin import AdminSite, ModelAdmin
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
from django.contrib.admin.models import ADDITION, DELETION, LogEntry
-from django.contrib.admin.options import TO_FIELD_VAR
+from django.contrib.admin.options import SOURCE_MODEL_VAR, TO_FIELD_VAR
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.admin.utils import quote
@@ -468,6 +468,126 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
response = self.client.post(reverse("admin:admin_views_article_add"), post_data)
self.assertContains(response, "title with a new\\nline")
+ def test_popup_add_POST_with_valid_source_model(self):
+ """
+ Popup add with a valid source_model returns a successful response.
+ """
+ post_data = {
+ IS_POPUP_VAR: "1",
+ SOURCE_MODEL_VAR: "admin_views.section",
+ "title": "Test Article",
+ "content": "some content",
+ "date_0": "2010-09-10",
+ "date_1": "14:55:39",
+ }
+ response = self.client.post(reverse("admin:admin_views_article_add"), post_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "data-popup-response")
+ messages = list(response.wsgi_request._messages)
+ self.assertEqual(len(messages), 0)
+
+ def test_popup_add_POST_with_optgroups(self):
+ """
+ Popup add with source_model containing optgroup choices includes
+ the optgroup in the response.
+ """
+ post_data = {
+ IS_POPUP_VAR: "1",
+ SOURCE_MODEL_VAR: "admin_views.section",
+ "title": "Test Article",
+ "content": "some content",
+ "date_0": "2010-09-10",
+ "date_1": "14:55:39",
+ }
+ response = self.client.post(
+ reverse("admin11:admin_views_article_add"), post_data
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "&quot;optgroup&quot;: &quot;Published&quot;")
+
+ def test_popup_add_POST_without_optgroups(self):
+ """
+ Popup add where source_model form exists but doesn't have the field
+ should work without crashing.
+ """
+ post_data = {
+ IS_POPUP_VAR: "1",
+ SOURCE_MODEL_VAR: "admin_views.section",
+ "title": "Test Article 2",
+ "content": "some content",
+ "date_0": "2010-09-10",
+ "date_1": "14:55:39",
+ }
+ # Use regular admin (not admin11) where Section doesn't have optgroups.
+ response = self.client.post(reverse("admin:admin_views_article_add"), post_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "data-popup-response")
+ self.assertNotContains(response, "&quot;optgroup&quot;")
+
+ def test_popup_add_POST_with_object_optgroups(self):
+ """
+ Popup add with source_model containing optgroups where the optgroup
+ keys are model instances (not strings) still serialize to strings.
+ """
+ post_data = {
+ IS_POPUP_VAR: "1",
+ SOURCE_MODEL_VAR: "admin_views.section",
+ "title": "Article 1",
+ "content": "some content",
+ "date_0": "2010-09-10",
+ "date_1": "14:55:39",
+ }
+ response = self.client.post(
+ reverse("admin12:admin_views_article_add"), post_data
+ )
+ self.assertEqual(response.status_code, 200)
+ # Check that optgroup is in the response with str() of Section instance
+ # The form uses Section.objects.all()[:2] which includes cls.s1
+ # ("Test section") as the first optgroup key (HTML encoded).
+ self.assertContains(response, "&quot;optgroup&quot;: &quot;Test section&quot;")
+
+ def test_popup_add_POST_with_dynamic_optgroups(self):
+ """
+ Popup add with source_model where optgroup field is added dynamically
+ in __init__. This ensures the implementation doesn't rely on accessing
+ the uninstantiated form class's _meta or fields, but instead properly
+ instantiates the form with get_form(request)() to access field info.
+ """
+ post_data = {
+ IS_POPUP_VAR: "1",
+ SOURCE_MODEL_VAR: "admin_views.section",
+ "title": "Item 1",
+ "content": "some content",
+ "date_0": "2010-09-10",
+ "date_1": "14:55:39",
+ }
+ response = self.client.post(
+ reverse("admin13:admin_views_article_add"), post_data
+ )
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "&quot;optgroup&quot;: &quot;Category A&quot;")
+
+ def test_popup_add_POST_with_invalid_source_model(self):
+ """
+ Popup add with an invalid source_model (non-existent app/model)
+ shows an error message instead of crashing.
+ """
+ post_data = {
+ IS_POPUP_VAR: "1",
+ SOURCE_MODEL_VAR: "admin_views.nonexistent",
+ "title": "Test Article",
+ "content": "some content",
+ "date_0": "2010-09-10",
+ "date_1": "14:55:39",
+ }
+ response = self.client.post(reverse("admin:admin_views_article_add"), post_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "data-popup-response")
+ messages = list(response.wsgi_request._messages)
+ self.assertEqual(len(messages), 1)
+ self.assertIn("admin_views.nonexistent", str(messages[0]))
+ self.assertIn("could not be found", str(messages[0]))
+
def test_basic_edit_POST(self):
"""
A smoke test to ensure POST on edit_view works.