Repository: wagtail/wagtail

Issue:
MultipleChooserPanel causes a crash when comparing page revisions
### Issue Summary

If MultipleChooserPanel is used in a Page model, then comparing page revisions will cause a crash.

### Steps to Reproduce

You can confirm this by modifying the bakery demo.

1. Modify BlogPage to use a MultipleChooserPanel for the authors instead of an InlinePanel.
2. Create a few drafts of a blog page.
3. Go to the page's history and click "Compare with previous version".

You'll get an AttributeError: 'BoundPanel' object has no attribute 'formset'

It looks like it's because `MultipleChooserPanel.BoundPanel.__init__()` assumes that self.formset exists.  However, when we're comparing changes, the parent `InlinePanel.BoundPanel.__init__()` will return early.

Shouldn't MultipleChooserPanel also check if self.form is None?



Discussion:

Resolving patch:
diff --git a/wagtail/admin/panels/multiple_chooser_panel.py b/wagtail/admin/panels/multiple_chooser_panel.py
--- a/wagtail/admin/panels/multiple_chooser_panel.py
+++ b/wagtail/admin/panels/multiple_chooser_panel.py
@@ -25,13 +25,15 @@ class BoundPanel(InlinePanel.BoundPanel):
 
         def __init__(self, *args, **kwargs):
             super().__init__(*args, **kwargs)
-            self.chooser_widget = self.formset.empty_form.fields[
-                self.panel.chooser_field_name
-            ].widget
-            self.js_context = JSContext()
-            self.chooser_widget_telepath_definition = self.js_context.pack(
-                self.chooser_widget
-            )
+
+            if self.form:
+                self.chooser_widget = self.formset.empty_form.fields[
+                    self.panel.chooser_field_name
+                ].widget
+                self.js_context = JSContext()
+                self.chooser_widget_telepath_definition = self.js_context.pack(
+                    self.chooser_widget
+                )
 
         def get_context_data(self, parent_context=None):
             context = super().get_context_data(parent_context)


Test patch:
diff --git a/wagtail/admin/tests/test_edit_handlers.py b/wagtail/admin/tests/test_edit_handlers.py
--- a/wagtail/admin/tests/test_edit_handlers.py
+++ b/wagtail/admin/tests/test_edit_handlers.py
@@ -47,6 +47,7 @@
     EventPageChooserModel,
     EventPageSpeaker,
     FormPageWithRedirect,
+    GalleryPage,
     PageChooserModel,
     RestaurantPage,
     RestaurantTag,
@@ -1885,6 +1886,31 @@ def test_can_render_panel(self):
         self.assertContains(response, 'chooserFieldName: "image"')
 
 
+class TestMultipleChooserPanelGetComparison(TestCase):
+    fixtures = ["test.json"]
+
+    def setUp(self):
+        self.request = RequestFactory().get("/")
+        user = AnonymousUser()  # technically, Anonymous users cannot access the admin
+        self.request.user = user
+        self.page = GalleryPage(title="Test page")
+        parent_page = Page.objects.get(id=2)
+        parent_page.add_child(instance=self.page)
+
+    def test_get_comparison(self):
+        # Test whether the InlinePanel passes it's label in get_comparison
+
+        comparison = (
+            self.page.get_edit_handler()
+            .get_bound_panel(instance=self.page, request=self.request)
+            .get_comparison()
+        )
+
+        comparison = [comp(self.page, self.page) for comp in comparison]
+        field_labels = [comp.field_label() for comp in comparison]
+        self.assertIn("Gallery images", field_labels)
+
+
 class TestPanelIcons(WagtailTestUtils, TestCase):
     def setUp(self):
         self.user = self.login()