From e040008f9ff9bc74d587a222c0bfad1e75faa847 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Wed, 2 Sep 2015 16:49:26 +0100 Subject: [PATCH 1/2] Failing test for #1418 --- wagtail/wagtailcore/tests/test_page_model.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wagtail/wagtailcore/tests/test_page_model.py b/wagtail/wagtailcore/tests/test_page_model.py index 7af85f8781..e54168b583 100644 --- a/wagtail/wagtailcore/tests/test_page_model.py +++ b/wagtail/wagtailcore/tests/test_page_model.py @@ -1,3 +1,4 @@ +import unittest import datetime import json @@ -423,6 +424,7 @@ class TestCopyPage(TestCase): self.assertEqual(new_christmas_event.advert_placements.count(), 1, "Child objects defined on the superclass weren't copied") self.assertEqual(christmas_event.advert_placements.count(), 1, "Child objects defined on the superclass were removed from the original page") + @unittest.expectedFailure def test_copy_page_copies_revisions(self): christmas_event = EventPage.objects.get(url_path='/home/events/christmas/') christmas_event.save_revision() @@ -448,6 +450,11 @@ class TestCopyPage(TestCase): self.assertEqual(new_revision_content['pk'], new_christmas_event.id) self.assertEqual(new_revision_content['speakers'][0]['page'], new_christmas_event.id) + # Also, check that the child objects in the new revision are given new IDs + old_speakers_ids = set(christmas_event.speakers.values_list('id', flat=True)) + new_speakers_ids = set(speaker['pk'] for speaker in new_revision_content['speakers']) + self.assertFalse(old_speakers_ids.intersection(new_speakers_ids), "Child objects in revisions were not given a new primary key") + def test_copy_page_copies_revisions_and_doesnt_submit_for_moderation(self): christmas_event = EventPage.objects.get(url_path='/home/events/christmas/') christmas_event.save_revision(submitted_for_moderation=True) From dd4db131802c4198512d00bafe50f1992f09f0e6 Mon Sep 17 00:00:00 2001 From: Karl Hobley Date: Wed, 2 Sep 2015 16:51:55 +0100 Subject: [PATCH 2/2] Remap primary keys of child objects in revisions when copying page --- wagtail/wagtailcore/models.py | 20 ++++++++++++++++++-- wagtail/wagtailcore/tests/test_page_model.py | 2 -- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index 4f821613e7..7c59fd9a26 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import logging import json +from collections import defaultdict from modelcluster.models import ClusterableModel, get_all_child_relations @@ -810,18 +811,27 @@ class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, index.Indexed else: page_copy = self.add_sibling(instance=page_copy) + # A dict that maps child objects to their new ids + # Used to remap child object ids in revisions + child_object_id_map = defaultdict(dict) + # Copy child objects specific_self = self.specific for child_relation in get_all_child_relations(specific_self): + accessor_name = child_relation.get_accessor_name() parental_key_name = child_relation.field.attname - child_objects = getattr(specific_self, child_relation.get_accessor_name(), None) + child_objects = getattr(specific_self, accessor_name, None) if child_objects: for child_object in child_objects.all(): + old_pk = child_object.pk child_object.pk = None setattr(child_object, parental_key_name, page_copy.id) child_object.save() + # Add mapping to new primary key (so we can apply this change to revisions) + child_object_id_map[accessor_name][old_pk] = child_object.pk + # Copy revisions if copy_revisions: for revision in self.revisions.all(): @@ -835,8 +845,9 @@ class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, index.Indexed revision_content['pk'] = page_copy.pk for child_relation in get_all_child_relations(specific_self): + accessor_name = child_relation.get_accessor_name() try: - child_objects = revision_content[child_relation.get_accessor_name()] + child_objects = revision_content[accessor_name] except KeyError: # KeyErrors are possible if the revision was created # before this child relation was added to the database @@ -845,6 +856,11 @@ class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, index.Indexed for child_object in child_objects: child_object[child_relation.field.name] = page_copy.pk + # Remap primary key to copied versions + # If the primary key is not recognised (eg, the child object has been deleted from the database) + # set the primary key to None + child_object['pk'] = child_object_id_map[accessor_name].get(child_object['pk'], None) + revision.content_json = json.dumps(revision_content) # Save diff --git a/wagtail/wagtailcore/tests/test_page_model.py b/wagtail/wagtailcore/tests/test_page_model.py index e54168b583..c9c81f0043 100644 --- a/wagtail/wagtailcore/tests/test_page_model.py +++ b/wagtail/wagtailcore/tests/test_page_model.py @@ -1,4 +1,3 @@ -import unittest import datetime import json @@ -424,7 +423,6 @@ class TestCopyPage(TestCase): self.assertEqual(new_christmas_event.advert_placements.count(), 1, "Child objects defined on the superclass weren't copied") self.assertEqual(christmas_event.advert_placements.count(), 1, "Child objects defined on the superclass were removed from the original page") - @unittest.expectedFailure def test_copy_page_copies_revisions(self): christmas_event = EventPage.objects.get(url_path='/home/events/christmas/') christmas_event.save_revision()