diff options
| author | devin13cox <36718613+devin13cox@users.noreply.github.com> | 2024-04-02 18:00:25 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-02 22:00:25 -0300 |
| commit | 8665cf03d79c4b6222514c5943ccf3863a19cf08 (patch) | |
| tree | 6d818877b856027bbee43b5a9ef25dac4c702b4f | |
| parent | 888b9042b3598bab6557c62de82505eec9ea62ed (diff) | |
Fixed #35330 -- Fixed the update of related widgets when the referenced model is camel case named.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
| -rw-r--r-- | django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html | 2 | ||||
| -rw-r--r-- | django/contrib/admin/widgets.py | 1 | ||||
| -rw-r--r-- | tests/admin_views/admin.py | 8 | ||||
| -rw-r--r-- | tests/admin_views/models.py | 12 | ||||
| -rw-r--r-- | tests/admin_views/test_related_object_lookups.py | 42 | ||||
| -rw-r--r-- | tests/admin_widgets/tests.py | 22 |
6 files changed, 86 insertions, 1 deletions
diff --git a/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html b/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html index 8e4356a95c..99b20545af 100644 --- a/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html +++ b/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html @@ -1,5 +1,5 @@ {% load i18n static %} -<div class="related-widget-wrapper" {% if not model_has_limit_choices_to %}data-model-ref="{{ model }}"{% endif %}> +<div class="related-widget-wrapper" {% if not model_has_limit_choices_to %}data-model-ref="{{ model_name }}"{% endif %}> {{ rendered_widget }} {% block links %} {% spaceless %} diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py index 216b87671b..260ff33ca5 100644 --- a/django/contrib/admin/widgets.py +++ b/django/contrib/admin/widgets.py @@ -329,6 +329,7 @@ class RelatedFieldWidgetWrapper(forms.Widget): "name": name, "url_params": url_params, "model": rel_opts.verbose_name, + "model_name": rel_opts.model_name, "can_add_related": self.can_add_related, "can_change_related": self.can_change_related, "can_delete_related": self.can_delete_related, diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py index c157e70505..0ea64d594a 100644 --- a/tests/admin_views/admin.py +++ b/tests/admin_views/admin.py @@ -33,6 +33,8 @@ from .models import ( Book, Bookmark, Box, + CamelCaseModel, + CamelCaseRelatedModel, Category, Chapter, ChapterXtra1, @@ -1181,6 +1183,10 @@ class SquareAdmin(admin.ModelAdmin): readonly_fields = ("area",) +class CamelCaseAdmin(admin.ModelAdmin): + filter_horizontal = ["m2m"] + + site = admin.AdminSite(name="admin") site.site_url = "/my-site-url/" site.register(Article, ArticleAdmin) @@ -1305,6 +1311,8 @@ site.register(Box) site.register(Country, CountryAdmin) site.register(Traveler, TravelerAdmin) site.register(Square, SquareAdmin) +site.register(CamelCaseModel) +site.register(CamelCaseRelatedModel, CamelCaseAdmin) # Register core models we need in our tests site.register(User, UserAdmin) diff --git a/tests/admin_views/models.py b/tests/admin_views/models.py index 341e5aaed0..812505de82 100644 --- a/tests/admin_views/models.py +++ b/tests/admin_views/models.py @@ -1155,3 +1155,15 @@ class Square(models.Model): class Meta: required_db_features = {"supports_stored_generated_columns"} + + +class CamelCaseModel(models.Model): + interesting_name = models.CharField(max_length=100) + + def __str__(self): + return self.interesting_name + + +class CamelCaseRelatedModel(models.Model): + m2m = models.ManyToManyField(CamelCaseModel, related_name="m2m") + fk = models.ForeignKey(CamelCaseModel, on_delete=models.CASCADE, related_name="fk") diff --git a/tests/admin_views/test_related_object_lookups.py b/tests/admin_views/test_related_object_lookups.py index 145bf0d6de..955bebea9e 100644 --- a/tests/admin_views/test_related_object_lookups.py +++ b/tests/admin_views/test_related_object_lookups.py @@ -76,3 +76,45 @@ class SeleniumTests(AdminSeleniumTestCase): with self.subTest(link_id): link = self.selenium.find_element(By.XPATH, f'//*[@id="{link_id}"]') self.assertIsNone(link.get_attribute("aria-disabled")) + + def test_related_object_update_with_camel_casing(self): + from selenium.webdriver.common.by import By + + def _get_HTML_inside_element_by_id(id_): + return self.selenium.find_element(By.ID, id_).get_attribute("innerHTML") + + add_url = reverse("admin:admin_views_camelcaserelatedmodel_add") + self.selenium.get(self.live_server_url + add_url) + interesting_name = "A test name" + + # Add a new CamelCaseModel using the "+" icon next to the "fk" field. + self.selenium.find_element(By.ID, "add_id_fk").click() + + # Switch to the add popup window. + self.wait_for_and_switch_to_popup() + + # Find the "interesting_name" field and enter a value, then save it. + self.selenium.find_element(By.ID, "id_interesting_name").send_keys( + interesting_name + ) + self.selenium.find_element(By.NAME, "_save").click() + + # Return to the main window. + self.wait_until(lambda d: len(d.window_handles) == 1, 1) + self.selenium.switch_to.window(self.selenium.window_handles[0]) + + # Check that both the "Available" m2m box and the "Fk" dropdown now + # include the newly added CamelCaseModel instance. + self.assertHTMLEqual( + self.selenium.find_element(By.ID, "id_fk"), + f""" + <option value="" selected="">---------</option> + <option value="1" selected>{interesting_name}</option> + """, + ) + self.assertHTMLEqual( + self.selenium.find_element(By.ID, "id_m2m_from"), + f""" + <option value="1">{interesting_name}</option> + """, + ) diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py index 76a47e4868..4d18849692 100644 --- a/tests/admin_widgets/tests.py +++ b/tests/admin_widgets/tests.py @@ -937,6 +937,28 @@ class RelatedFieldWidgetWrapperTests(SimpleTestCase): # Related item links are present. self.assertIn("<a ", output) + def test_data_model_ref_when_model_name_is_camel_case(self): + rel = VideoStream._meta.get_field("release_event").remote_field + widget = forms.Select() + wrapper = widgets.RelatedFieldWidgetWrapper(widget, rel, widget_admin_site) + self.assertIs(wrapper.is_hidden, False) + context = wrapper.get_context("release_event", None, {}) + self.assertEqual(context["model"], "release event") + self.assertEqual(context["model_name"], "releaseevent") + output = wrapper.render("stream", "value") + expected = """ + <div class="related-widget-wrapper" data-model-ref="releaseevent"> + <select name="stream"> + </select> + <a class="related-widget-wrapper-link add-related" id="add_id_stream" + data-popup="yes" title="Add another release event" + href="/admin_widgets/releaseevent/add/?_to_field=album&_popup=1"> + <img src="/static/admin/img/icon-addlink.svg" alt="" width="20" height="20"> + </a> + </div> + """ + self.assertHTMLEqual(output, expected) + @override_settings(ROOT_URLCONF="admin_widgets.urls") class AdminWidgetSeleniumTestCase(AdminSeleniumTestCase): |
