From bd6a7c768f9ce030e4b502c8ceaace2f51077cf6 Mon Sep 17 00:00:00 2001 From: Matt Westcott Date: Thu, 29 Oct 2015 17:16:07 +0000 Subject: [PATCH] Use the live database record as the result of get_latest_revision_as_page if there are no draft edits. This ensures that the live database state will be reflected in the page editor, which provides two benefits: * any changes made directly at the model / database level (e.g. automated data imports) will be visible in the editor * inline child objects will be associated with their actual database IDs even if this information is missing from the revision record. This ensures that their IDs will be preserved on next publish, rather than the records being deleted and recreated (#1853) --- wagtail/tests/testapp/models.py | 5 ++++ .../wagtailadmin/tests/test_pages_views.py | 27 +++++++++++++++++++ wagtail/wagtailcore/models.py | 11 +++++++- wagtail/wagtailcore/tests/test_page_model.py | 7 +++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/wagtail/tests/testapp/models.py b/wagtail/tests/testapp/models.py index a0bdbc394a..772c561b30 100644 --- a/wagtail/tests/testapp/models.py +++ b/wagtail/tests/testapp/models.py @@ -118,6 +118,11 @@ class RelatedLink(LinkFields): class SimplePage(Page): content = models.TextField() + content_panels = [ + FieldPanel('title', classname="full title"), + FieldPanel('content'), + ] + class PageWithOldStyleRouteMethod(Page): """ diff --git a/wagtail/wagtailadmin/tests/test_pages_views.py b/wagtail/wagtailadmin/tests/test_pages_views.py index becc42cc22..31c7f1e4ab 100644 --- a/wagtail/wagtailadmin/tests/test_pages_views.py +++ b/wagtail/wagtailadmin/tests/test_pages_views.py @@ -900,6 +900,33 @@ class TestPageEdit(TestCase, WagtailTestUtils): self.assertTemplateUsed(response, 'tests/simple_page.html') self.assertEqual(response.context['request'].site.hostname, 'childpage.example.com') + def test_editor_picks_up_direct_model_edits(self): + # If a page has no draft edits, the editor should show the version from the live database + # record rather than the latest revision record. This ensures that the edit interface + # reflects any changes made directly on the model. + self.child_page.title = "This title only exists on the live database record" + self.child_page.save() + + response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, ))) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "This title only exists on the live database record") + + def test_editor_does_not_pick_up_direct_model_edits_when_draft_edits_exist(self): + # If a page has draft edits, we should always show those in the editor, not the live + # database record + self.child_page.content = "Some content with a draft edit" + self.child_page.save_revision() + + # make an independent change to the live database record + self.child_page = SimplePage.objects.get(id=self.child_page.id) + self.child_page.title = "This title only exists on the live database record" + self.child_page.save() + + response = self.client.get(reverse('wagtailadmin_pages:edit', args=(self.child_page.id, ))) + self.assertEqual(response.status_code, 200) + self.assertNotContains(response, "This title only exists on the live database record") + self.assertContains(response, "Some content with a draft edit") + class TestPageEditReordering(TestCase, WagtailTestUtils): def setUp(self): diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index ccdafbcab7..370bb2d04d 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -474,6 +474,15 @@ class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, index.Indexed return self.revisions.order_by('-created_at', '-id').first() def get_latest_revision_as_page(self): + if not self.has_unpublished_changes: + # Use the live database copy in preference to the revision record, as: + # 1) this will pick up any changes that have been made directly to the model, + # such as automated data imports; + # 2) it ensures that inline child objects pick up real database IDs even if + # those are absent from the revision data. (If this wasn't the case, the child + # objects would be recreated with new IDs on next publish - see #1853) + return self.specific + latest_revision = self.get_latest_revision() if latest_revision: @@ -826,7 +835,7 @@ class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, index.Indexed # Create a new revision # This code serves a few purposes: - # * It makes sure update_attrs gets applied to the latest revision so the changes are reflected in the editor + # * It makes sure update_attrs gets applied to the latest revision # * It bumps the last_revision_created_at value so the new page gets ordered as if it was just created # * It sets the user of the new revision so it's possible to see who copied the page by looking at its history latest_revision = page_copy.get_latest_revision_as_page() diff --git a/wagtail/wagtailcore/tests/test_page_model.py b/wagtail/wagtailcore/tests/test_page_model.py index 5483b31cac..4f3f963b35 100644 --- a/wagtail/wagtailcore/tests/test_page_model.py +++ b/wagtail/wagtailcore/tests/test_page_model.py @@ -447,6 +447,13 @@ class TestCopyPage(TestCase): self.assertEqual(latest_revision.title, "New christmas event") self.assertEqual(latest_revision.slug, 'new-christmas-event') + # get_latest_revision_as_page might bypass the revisions table if it determines + # that there are no draft edits since publish - so retrieve it explicitly from the + # revision data, to ensure it's been updated there too + latest_revision = new_christmas_event.get_latest_revision().as_page_object() + self.assertEqual(latest_revision.title, "New christmas event") + self.assertEqual(latest_revision.slug, 'new-christmas-event') + # Check that the ids within the revision were updated correctly new_revision = new_christmas_event.revisions.first() new_revision_content = json.loads(new_revision.content_json)