From 368049b83553bca2523ec9b46fc9ae7731f23e0c Mon Sep 17 00:00:00 2001 From: nmorduch <1051450+nmorduch@users.noreply.github.com> Date: Tue, 22 Jun 2021 11:24:54 -0400 Subject: [PATCH] Warn if StreamField doesn't use StreamFieldPanel (#7276) --- CHANGELOG.txt | 1 + docs/releases/2.14.rst | 1 + wagtail/admin/checks.py | 49 +++++++++++++++++++++++ wagtail/admin/tests/test_edit_handlers.py | 26 +++++++++++- 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 32ba543ea8..23e5acbc72 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -11,6 +11,7 @@ Changelog * Added locale selector when choosing translatable snippets (Karl Hobley) * Added `WAGTAIL_WORKFLOW_ENABLED` setting for enabling / disabling moderation workflows globally (Matt Westcott) * Allow specifying `max_width` and `max_height` on EmbedBlock (Petr DlouhĂ˝) + * Add warning when StreamField is used without a StreamFieldPanel (Naomi Morduch Toubman) * Fix: Invalid filter values for foreign key fields in the API now give an error instead of crashing (Tidjani Dia) * Fix: Ordering specified in `construct_explorer_page_queryset` hook is now taken into account again by the page explorer API (Andre Fonseca) * Fix: Deleting a page from its listing view no longer results in a 404 error (Tidjani Dia) diff --git a/docs/releases/2.14.rst b/docs/releases/2.14.rst index adafcf7d2b..f6101d0634 100644 --- a/docs/releases/2.14.rst +++ b/docs/releases/2.14.rst @@ -19,6 +19,7 @@ Other features * Added locale selector when choosing translatable snippets (Karl Hobley) * Added ``WAGTAIL_WORKFLOW_ENABLED`` setting for enabling / disabling moderation workflows globally (Matt Westcott) * Allow specifying ``max_width`` and ``max_height`` on EmbedBlock (Petr DlouhĂ˝) + * Add warning when StreamField is used without a StreamFieldPanel (Naomi Morduch Toubman) Bug fixes ~~~~~~~~~ diff --git a/wagtail/admin/checks.py b/wagtail/admin/checks.py index 00dc9264a6..a52e19480f 100644 --- a/wagtail/admin/checks.py +++ b/wagtail/admin/checks.py @@ -1,6 +1,7 @@ import os from django.core.checks import Error, Warning, register +from django.core.exceptions import FieldDoesNotExist @register() @@ -151,3 +152,51 @@ There are no default tabs on non-Page models so there will be no \ errors.append(error) return errors + + +@register('panels') +def panel_type_check(app_configs, **kwargs): + from wagtail.core.models import get_page_models + + errors = [] + + for cls in get_page_models(): + errors += traverse_edit_handlers(cls.get_edit_handler()) + + return errors + + +def traverse_edit_handlers(edit_handler): + errors = [] + + try: + for child in edit_handler.children: + errors += traverse_edit_handlers(child) + except AttributeError: + error = check_stream_field_panel_type(edit_handler) + if error: + errors.append(error) + + return errors + + +def check_stream_field_panel_type(edit_handler): + from wagtail.admin.edit_handlers import StreamFieldPanel + from wagtail.core.fields import StreamField + + try: + db_field = getattr(edit_handler, 'db_field', None) + if isinstance(db_field, StreamField) and not isinstance(edit_handler, StreamFieldPanel): + return Warning( + "{model}.{field_name} is a StreamField, but uses {edit_handler}".format( + model=edit_handler.model.__name__, + field_name=edit_handler.field_name, + edit_handler=edit_handler.__class__.__name__), + hint="Ensure that it uses a StreamFieldPanel, or change the field type", + obj=edit_handler.model, + id='wagtailadmin.W003' + ) + except FieldDoesNotExist: + # Doesn't check any fields not on the model, such as in + # wagtail.tests.testapp.modelsFormClassAdditionalFieldPage + pass diff --git a/wagtail/admin/tests/test_edit_handlers.py b/wagtail/admin/tests/test_edit_handlers.py index b6c62e2a91..7c5f1901a7 100644 --- a/wagtail/admin/tests/test_edit_handlers.py +++ b/wagtail/admin/tests/test_edit_handlers.py @@ -24,8 +24,8 @@ from wagtail.core.models import Comment, CommentReply, Page, Site from wagtail.images.edit_handlers import ImageChooserPanel from wagtail.tests.testapp.forms import ValidatedPageForm from wagtail.tests.testapp.models import ( - EventPage, EventPageChooserModel, EventPageSpeaker, PageChooserModel, RestaurantPage, - RestaurantTag, SimplePage, ValidatedPage) + DefaultStreamPage, EventPage, EventPageChooserModel, EventPageSpeaker, PageChooserModel, + RestaurantPage, RestaurantTag, SimplePage, ValidatedPage) from wagtail.tests.utils import WagtailTestUtils @@ -217,6 +217,28 @@ class TestPageEditHandlers(TestCase): self.assertEqual(errors, [invalid_base_form, invalid_edit_handler]) + @clear_edit_handler(DefaultStreamPage) + def test_check_invalid_streamfield_edit_handler(self): + """ + Set the edit handler for body (a StreamField) to be + a FieldPanel instead of a StreamFieldPanel. + Check that the correct warning is raised. + """ + + invalid_edit_handler = checks.Warning( + "DefaultStreamPage.body is a StreamField, but uses FieldPanel", + hint="Ensure that it uses a StreamFieldPanel, or change the field type", + obj=DefaultStreamPage, + id='wagtailadmin.W003') + + with mock.patch.object(DefaultStreamPage, 'content_panels', new=[FieldPanel('body')]): + checks_result = checks.run_checks(tags=['panels']) + + # Only look at warnings for DefaultStreamPage + warning = [warning for warning in checks_result if warning.obj == DefaultStreamPage] + + self.assertEqual(warning, [invalid_edit_handler]) + @clear_edit_handler(ValidatedPage) def test_custom_edit_handler_form_class(self): """