From 9976f3d4b80cfb2e6f4c998438622b78eb1ac53e Mon Sep 17 00:00:00 2001 From: David Sanders Date: Wed, 21 Sep 2022 22:56:22 +1000 Subject: [PATCH] Fixed #34025 -- Fixed selecting ModelAdmin.autocomplete_fields after adding/changing related instances via popups. Regression in c72f6f36c13a21f6db3d4f85d2d3cec87bad45e6. Thanks Alexandre da Silva for the report. --- .../admin/js/admin/RelatedObjectLookups.js | 3 +- docs/releases/4.1.2.txt | 4 ++ tests/admin_views/admin.py | 12 +++- tests/admin_views/tests.py | 61 ++++++++++++------- 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js index 5c9c0d833d..752dcad7bf 100644 --- a/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js @@ -96,7 +96,8 @@ // Extract the model from the popup url '...//add/' or // '...///change/' depending the action (add or change). const modelName = path.split('/')[path.split('/').length - (objId ? 4 : 3)]; - const selectsRelated = document.querySelectorAll(`[data-model-ref="${modelName}"] select`); + // Exclude autocomplete selects. + const selectsRelated = document.querySelectorAll(`[data-model-ref="${modelName}"] select:not(.admin-autocomplete)`); selectsRelated.forEach(function(select) { if (currentSelect === select) { diff --git a/docs/releases/4.1.2.txt b/docs/releases/4.1.2.txt index 6a473c16eb..809d583a78 100644 --- a/docs/releases/4.1.2.txt +++ b/docs/releases/4.1.2.txt @@ -23,3 +23,7 @@ Bugfixes * Fixed a regression in Django 4.1 that caused a ``QuerySet.values()/values_list()`` crash on ``ArrayAgg()`` and ``JSONBAgg()`` (:ticket:`34016`). + +* Fixed a bug in Django 4.1 that caused :attr:`.ModelAdmin.autocomplete_fields` + to be incorrectly selected after adding/changing related instances via popups + (:ticket:`34025`). diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index 996f7d5afc..9241034ffb 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -1165,6 +1165,14 @@ class GetFormsetsArgumentCheckingAdmin(admin.ModelAdmin): return super().get_formsets_with_inlines(request, obj) +class CountryAdmin(admin.ModelAdmin): + search_fields = ["name"] + + +class TravelerAdmin(admin.ModelAdmin): + autocomplete_fields = ["living_country"] + + site = admin.AdminSite(name="admin") site.site_url = "/my-site-url/" site.register(Article, ArticleAdmin) @@ -1286,8 +1294,8 @@ site.register(ExplicitlyProvidedPK, GetFormsetsArgumentCheckingAdmin) site.register(ImplicitlyGeneratedPK, GetFormsetsArgumentCheckingAdmin) site.register(UserProxy) site.register(Box) -site.register(Country) -site.register(Traveler) +site.register(Country, CountryAdmin) +site.register(Traveler, TravelerAdmin) # Register core models we need in our tests site.register(User, UserAdmin) diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index 30010707d9..cd5b8ed836 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -6317,18 +6317,24 @@ class SeleniumTests(AdminSeleniumTestCase): finally: self.selenium.set_window_size(current_size["width"], current_size["height"]) - def test_updating_related_objects_updates_fk_selects(self): + def test_updating_related_objects_updates_fk_selects_except_autocompletes(self): from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import Select born_country_select_id = "id_born_country" living_country_select_id = "id_living_country" + living_country_select2_textbox_id = "select2-id_living_country-container" favorite_country_to_vacation_select_id = "id_favorite_country_to_vacation" continent_select_id = "id_continent" def _get_HTML_inside_element_by_id(id_): return self.selenium.find_element(By.ID, id_).get_attribute("innerHTML") + def _get_text_inside_element_by_selector(selector): + return self.selenium.find_element(By.CSS_SELECTOR, selector).get_attribute( + "innerText" + ) + self.admin_login( username="super", password="secret", login_url=reverse("admin:index") ) @@ -6353,12 +6359,16 @@ class SeleniumTests(AdminSeleniumTestCase): """, ) - self.assertHTMLEqual( - _get_HTML_inside_element_by_id(living_country_select_id), - """ - - - """, + # Argentina isn't added to the living_country select nor selected by + # the select2 widget. + self.assertEqual( + _get_text_inside_element_by_selector(f"#{living_country_select_id}"), "" + ) + self.assertEqual( + _get_text_inside_element_by_selector( + f"#{living_country_select2_textbox_id}" + ), + "", ) # Argentina won't appear because favorite_country_to_vacation field has # limit_choices_to. @@ -6386,13 +6396,18 @@ class SeleniumTests(AdminSeleniumTestCase): """, ) - self.assertHTMLEqual( - _get_HTML_inside_element_by_id(living_country_select_id), - """ - - - - """, + + # Spain is added to the living_country select and it's also selected by + # the select2 widget. + self.assertEqual( + _get_text_inside_element_by_selector(f"#{living_country_select_id} option"), + "Spain", + ) + self.assertEqual( + _get_text_inside_element_by_selector( + f"#{living_country_select2_textbox_id}" + ), + "Spain", ) # Spain won't appear because favorite_country_to_vacation field has # limit_choices_to. @@ -6422,13 +6437,17 @@ class SeleniumTests(AdminSeleniumTestCase): """, ) - self.assertHTMLEqual( - _get_HTML_inside_element_by_id(living_country_select_id), - """ - - - - """, + # Italy is added to the living_country select and it's also selected by + # the select2 widget. + self.assertEqual( + _get_text_inside_element_by_selector(f"#{living_country_select_id} option"), + "Italy", + ) + self.assertEqual( + _get_text_inside_element_by_selector( + f"#{living_country_select2_textbox_id}" + ), + "Italy", ) # favorite_country_to_vacation field has no options. self.assertHTMLEqual(