diff --git a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js index 32e3f5b840..bc3accea37 100644 --- a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js @@ -96,8 +96,8 @@ // Extract the model from the popup url '.../<model>/add/' or // '.../<model>/<id>/change/' depending the action (add or change). const modelName = path.split('/')[path.split('/').length - (objId ? 4 : 3)]; - // Exclude autocomplete selects. - const selectsRelated = document.querySelectorAll(`[data-model-ref="${modelName}"] select:not(.admin-autocomplete)`); + // Select elements with a specific model reference and context of "available-source". + const selectsRelated = document.querySelectorAll(`[data-model-ref="${modelName}"] [data-context="available-source"]`); selectsRelated.forEach(function(select) { if (currentSelect === select) { diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 260ff33ca5..00e92bf42d 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -272,6 +272,8 @@ class RelatedFieldWidgetWrapper(forms.Widget): 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 # XXX: The deletion UX can be confusing when dealing with cascading deletion. cascade = getattr(rel, "on_delete", None) is CASCADE diff --git a/tests/admin_views/test_related_object_lookups.py b/tests/admin_views/test_related_object_lookups.py index c10a5568d5..761819a50f 100644 --- a/tests/admin_views/test_related_object_lookups.py +++ b/tests/admin_views/test_related_object_lookups.py @@ -110,6 +110,9 @@ class SeleniumTests(AdminSeleniumTestCase): <option value="1" selected>{interesting_name}</option> """, ) + # Check the newly added instance is not also added in the "to" box. + m2m_to = self.selenium.find_element(By.ID, "id_m2m_to") + self.assertHTMLEqual(m2m_to.get_attribute("innerHTML"), "") m2m_box = self.selenium.find_element(By.ID, "id_m2m_from") self.assertHTMLEqual( m2m_box.get_attribute("innerHTML"), diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 4d18849692..6f009a6f3f 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -948,7 +948,7 @@ class RelatedFieldWidgetWrapperTests(SimpleTestCase): output = wrapper.render("stream", "value") expected = """ <div class="related-widget-wrapper" data-model-ref="releaseevent"> - <select name="stream"> + <select name="stream" data-context="available-source"> </select> <a class="related-widget-wrapper-link add-related" id="add_id_stream" data-popup="yes" title="Add another release event" diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py index de8d26ae46..e8b59ed0bf 100644 --- a/tests/modeladmin/tests.py +++ b/tests/modeladmin/tests.py @@ -662,7 +662,8 @@ class ModelAdminTests(TestCase): self.assertHTMLEqual( str(form["main_band"]), '<div class="related-widget-wrapper" data-model-ref="band">' - '<select name="main_band" id="id_main_band" required>' + '<select data-context="available-source" ' + 'name="main_band" id="id_main_band" required>' '<option value="" selected>---------</option>' '<option value="%d">The Beatles</option>' '<option value="%d">The Doors</option>' @@ -685,7 +686,8 @@ class ModelAdminTests(TestCase): self.assertHTMLEqual( str(form["main_band"]), '<div class="related-widget-wrapper" data-model-ref="band">' - '<select name="main_band" id="id_main_band" required>' + '<select data-context="available-source" ' + 'name="main_band" id="id_main_band" required>' '<option value="" selected>---------</option>' '<option value="%d">The Doors</option>' "</select></div>" % self.band.id, @@ -779,7 +781,8 @@ class ModelAdminTests(TestCase): type(cmafa.base_fields["main_band"].widget.widget), AdminRadioSelect ) self.assertEqual( - cmafa.base_fields["main_band"].widget.attrs, {"class": "radiolist inline"} + cmafa.base_fields["main_band"].widget.attrs, + {"class": "radiolist inline", "data-context": "available-source"}, ) self.assertEqual( list(cmafa.base_fields["main_band"].widget.choices), @@ -790,7 +793,8 @@ class ModelAdminTests(TestCase): type(cmafa.base_fields["opening_band"].widget.widget), AdminRadioSelect ) self.assertEqual( - cmafa.base_fields["opening_band"].widget.attrs, {"class": "radiolist"} + cmafa.base_fields["opening_band"].widget.attrs, + {"class": "radiolist", "data-context": "available-source"}, ) self.assertEqual( list(cmafa.base_fields["opening_band"].widget.choices),