瀏覽代碼

Documentation: Inline models optional Orderable, tests (#12034)

Fixes #11887
Bojan Mihelac 9 月之前
父節點
當前提交
5a1ba5abe2

+ 1 - 0
CHANGELOG.txt

@@ -25,6 +25,7 @@ Changelog
  * Docs: Document `restriction_type` field on PageViewRestriction (Shlomo Markowitz)
  * Docs: Document Wagtail's bug bounty policy (Jake Howard)
  * Docs: Fix incorrect Sphinx-style code references to use MyST style (Byron Peebles)
+ * Docs: Document the fact that `Orderable` is not required for inline panels (Bojan Mihelac)
  * Maintenance: Use `DjangoJSONEncoder` instead of custom `LazyStringEncoder` to serialize Draftail config (Sage Abdullah)
  * Maintenance: Refactor image chooser pagination to check `WAGTAILIMAGES_CHOOSER_PAGE_SIZE` at runtime (Matt Westcott)
  * Maintenance: Exclude the `client/scss` directory in Tailwind content config to speed up CSS compilation (Sage Abdullah)

+ 1 - 0
docs/releases/6.2.md

@@ -43,6 +43,7 @@ depth: 1
  * Document `restriction_type` field on PageViewRestriction (Shlomo Markowitz)
  * Document Wagtail's bug bounty policy (Jake Howard)
  * Fix incorrect Sphinx-style code references to use MyST style (Byron Peebles)
+ * Document the fact that `Orderable` is not required for inline panels (Bojan Mihelac)
 
 
 ### Maintenance

+ 1 - 4
docs/topics/pages.md

@@ -333,10 +333,7 @@ class BlogPage(Page):
 
 Wagtail allows the nesting of other models within a page. This is useful for creating repeated fields, such as related links or items to display in a carousel. Inline model content is also versioned with the rest of the page.
 
-Each inline model requires the following:
-
--   It must inherit from {class}`wagtail.models.Orderable`
--   It must have a `ParentalKey` to the parent model
+An inline model must have a `ParentalKey` pointing to the parent model. It can also inherit from {class}`wagtail.models.Orderable` to allow reordering of items in the admin interface.
 
 ````{note}
 The model inlining feature is provided by [django-modelcluster](https://github.com/wagtail/django-modelcluster) and the `ParentalKey` field type must be imported from there:

+ 47 - 0
wagtail/admin/tests/pages/test_create_page.py

@@ -1563,6 +1563,10 @@ class TestInlinePanelWithTags(WagtailTestUtils, TestCase):
             "comments-INITIAL_FORMS": 0,
             "comments-MIN_NUM_FORMS": 0,
             "comments-MAX_NUM_FORMS": 1000,
+            "social_links-TOTAL_FORMS": 0,
+            "social_links-INITIAL_FORMS": 0,
+            "social_links-MIN_NUM_FORMS": 0,
+            "social_links-MAX_NUM_FORMS": 1000,
         }
         response = self.client.post(
             reverse(
@@ -1578,6 +1582,49 @@ class TestInlinePanelWithTags(WagtailTestUtils, TestCase):
         self.assertEqual(new_page.addresses.first().tags.count(), 2)
 
 
+class TestNonOrderableInlinePanel(WagtailTestUtils, TestCase):
+    # https://github.com/wagtail/wagtail/issues/11887
+
+    def setUp(self):
+        self.root_page = Page.objects.get(id=2)
+        self.user = self.login()
+
+    def test_create(self):
+        post_data = {
+            "title": "Mr Benn",
+            "slug": "mr-benn",
+            "first_name": "William",
+            "last_name": "Benn",
+            "addresses-TOTAL_FORMS": 0,
+            "addresses-INITIAL_FORMS": 0,
+            "addresses-MIN_NUM_FORMS": 0,
+            "addresses-MAX_NUM_FORMS": 1000,
+            "action-publish": "Publish",
+            "comments-TOTAL_FORMS": 0,
+            "comments-INITIAL_FORMS": 0,
+            "comments-MIN_NUM_FORMS": 0,
+            "comments-MAX_NUM_FORMS": 1000,
+            "social_links-TOTAL_FORMS": 1,
+            "social_links-INITIAL_FORMS": 0,
+            "social_links-MIN_NUM_FORMS": 0,
+            "social_links-MAX_NUM_FORMS": 1000,
+            "social_links-0-url": "https://twitter.com/mrbenn",
+            "social_links-0-kind": "twitter",
+        }
+        response = self.client.post(
+            reverse(
+                "wagtailadmin_pages:add",
+                args=("tests", "personpage", self.root_page.id),
+            ),
+            post_data,
+        )
+        self.assertRedirects(
+            response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
+        )
+        new_page = PersonPage.objects.get(slug="mr-benn")
+        self.assertEqual(new_page.social_links.count(), 1)
+
+
 class TestInlinePanelNonFieldErrors(WagtailTestUtils, TestCase):
     """
     Test that non field errors will render for InlinePanels

+ 37 - 0
wagtail/admin/tests/test_edit_handlers.py

@@ -52,6 +52,7 @@ from wagtail.test.testapp.models import (
     FormPageWithRedirect,
     GalleryPage,
     PageChooserModel,
+    PersonPage,
     RestaurantPage,
     RestaurantTag,
     SimplePage,
@@ -1522,6 +1523,42 @@ class TestInlinePanel(WagtailTestUtils, TestCase):
             )
 
 
+class TestNonOrderableInlinePanel(WagtailTestUtils, TestCase):
+    fixtures = ["test.json"]
+
+    def setUp(self):
+        self.request = get_dummy_request()
+        user = AnonymousUser()  # technically, Anonymous users cannot access the admin
+        self.request.user = user
+
+    def test_render(self):
+        """
+        Check that the inline panel renders the panels set on the model
+        when no 'panels' parameter is passed in the InlinePanel definition
+        """
+        social_link_object_list = ObjectList(
+            [
+                InlinePanel(
+                    "social_links",
+                    label="Social Links",
+                )
+            ]
+        ).bind_to_model(PersonPage)
+        PersonPageForm = social_link_object_list.get_form_class()
+
+        person_page = PersonPage()
+        form = PersonPageForm(instance=person_page)
+        panel = social_link_object_list.get_bound_panel(
+            instance=person_page, form=form, request=self.request
+        )
+        result = panel.render_html()
+        # rendered panel must not contain hidden fields for ORDER
+        self.assertNotInHTML(
+            'id="id_social_links-__prefix__-ORDER"',
+            result,
+        )
+
+
 class TestInlinePanelGetComparison(TestCase):
     fixtures = ["test.json"]
 

+ 52 - 0
wagtail/test/testapp/migrations/0038_sociallink.py

@@ -0,0 +1,52 @@
+# Generated by Django 4.2.13 on 2024-06-12 12:06
+
+from django.db import migrations, models
+import django.db.models.deletion
+import modelcluster.fields
+import wagtail.search.index
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("tests", "0037_testpermissionedgenericsetting_and_more"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="SocialLink",
+            fields=[
+                (
+                    "id",
+                    models.AutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("url", models.URLField()),
+                (
+                    "kind",
+                    models.CharField(
+                        choices=[("twitter", "Twitter"), ("facebook", "Facebook")],
+                        max_length=30,
+                    ),
+                ),
+                (
+                    "person",
+                    modelcluster.fields.ParentalKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="social_links",
+                        to="tests.personpage",
+                        verbose_name="Person",
+                    ),
+                ),
+            ],
+            options={
+                "verbose_name": "Social link",
+                "verbose_name_plural": "Social links",
+            },
+            bases=(wagtail.search.index.Indexed, models.Model),
+        ),
+    ]

+ 24 - 0
wagtail/test/testapp/models.py

@@ -2097,6 +2097,7 @@ class PersonPage(Page):
             "Person",
         ),
         InlinePanel("addresses", label="Address"),
+        InlinePanel("social_links", label="Social links"),
     ]
 
     class Meta:
@@ -2133,6 +2134,29 @@ class AddressTag(TaggedItemBase):
     )
 
 
+class SocialLink(index.Indexed, ClusterableModel):
+    url = models.URLField()
+    kind = models.CharField(
+        max_length=30,
+        choices=[
+            ("twitter", "Twitter"),
+            ("facebook", "Facebook"),
+        ],
+    )
+    person = ParentalKey(
+        to="tests.PersonPage", related_name="social_links", verbose_name="Person"
+    )
+
+    panels = [
+        FieldPanel("url"),
+        FieldPanel("kind"),
+    ]
+
+    class Meta:
+        verbose_name = "Social link"
+        verbose_name_plural = "Social links"
+
+
 class RestaurantPage(Page):
     tags = ClusterTaggableManager(through="tests.TaggedRestaurant", blank=True)