浏览代码

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>
devin13cox 1 年之前
父节点
当前提交
8665cf03d7

+ 1 - 1
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 %}

+ 1 - 0
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,

+ 8 - 0
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)

+ 12 - 0
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")

+ 42 - 0
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>
+            """,
+        )

+ 22 - 0
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&amp;_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):