import: do not fail if page.parent is not found (#22889)

This commit is contained in:
Lauréline Guérin 2020-05-11 15:34:40 +02:00
parent 320bcd2bc1
commit 91b89e6ed5
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
5 changed files with 74 additions and 20 deletions

View File

@ -29,6 +29,7 @@ import subprocess
from django.apps import apps
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.fields import GenericRelation
@ -399,11 +400,21 @@ class Page(models.Model):
return serialized_page
@classmethod
def load_serialized_page(cls, json_page, snapshot=None):
def load_serialized_page(cls, json_page, snapshot=None, request=None):
json_page['model'] = 'data.page'
json_page['fields']['groups'] = [[x] for x in json_page['fields']['groups'] if isinstance(x, six.string_types)]
page, created = Page.objects.get_or_create(slug=json_page['fields']['slug'], snapshot=snapshot)
json_page['pk'] = page.id
parent_slug = json_page['fields'].get('parent') or []
if parent_slug and not Page.objects.filter(slug=parent_slug[0]).exists():
# parent not found, remove it and exclude page from navigation
json_page['fields'].pop('parent')
json_page['fields']['exclude_from_navigation'] = True
if request:
messages.warning(
request,
_('Unknown parent for page "%s"; parent has been reset and page was excluded from navigation.')
% json_page['fields']['title'])
page = next(serializers.deserialize('json', json.dumps([json_page]), ignorenonexistent=True))
page.object.snapshot = snapshot
page.save()
@ -414,7 +425,6 @@ class Page(models.Model):
else:
cell['fields']['page'] = page.object.natural_key()
# if there were cells, remove them
for cell in CellBase.get_cells(page_id=page.object.id):
cell.delete()
@ -433,20 +443,13 @@ class Page(models.Model):
cell.object.import_subobjects(cell_data)
@classmethod
def load_serialized_pages(cls, json_site):
def load_serialized_pages(cls, json_site, request=None):
cells = []
for json_page in json_site:
cls.load_serialized_page(json_page)
cls.load_serialized_page(json_page, request=request)
cells.extend(json_page.get('cells'))
cls.load_serialized_cells(cells)
# 2nd pass to set parents
for json_page in json_site:
if json_page.get('parent_slug'):
page = Page.objects.get(slug=json_page['fields']['slug'])
page.parent = Page.objects.get(slug=json_page.get('parent_slug'))
page.save()
@classmethod
def export_all_for_json(cls):
ordered_pages = Page.get_as_reordered_flat_hierarchy(cls.objects.all())

View File

@ -47,7 +47,7 @@ def export_site():
}
def import_site(data, if_empty=False, clean=False):
def import_site(data, if_empty=False, clean=False, request=None):
if isinstance(data, list):
# old export form with a list of pages, convert it to new dictionary
# format.
@ -80,7 +80,7 @@ def import_site(data, if_empty=False, clean=False):
MapLayer.load_serialized_objects(data.get('map-layers') or [])
Asset.load_serialized_objects(data.get('assets') or [])
Page.load_serialized_pages(data.get('pages') or [])
Page.load_serialized_pages(data.get('pages') or [], request=request)
if data.get('pwa'):
PwaSettings.load_serialized_settings(data['pwa'].get('settings'))

View File

@ -85,13 +85,14 @@ class SiteImportView(FormView):
return self.form_invalid(form)
try:
import_site(json_site)
import_site(json_site, request=self.request)
except MissingGroups as e:
form.add_error('site_json', force_text(e))
return self.form_invalid(form)
return super(SiteImportView, self).form_valid(form)
site_import = SiteImportView.as_view()

View File

@ -26,15 +26,13 @@ pytestmark = pytest.mark.django_db
@pytest.fixture
def some_data():
page = Page(title='One', slug='one')
page.save()
page = Page(title='Two', slug='two')
page.save()
page = Page(title='Three', slug='three')
page.save()
Page.objects.create(title='One', slug='one')
Page.objects.create(title='Two', slug='two')
page = Page.objects.create(title='Three', slug='three')
cell = TextCell(page=page, order=0, text='hello world', placeholder='content')
cell.save()
@pytest.fixture
def some_map_layers():
MapLayer(label='Foo', slug='foo', geojson_url='http://example.net/foo/').save()
@ -52,6 +50,7 @@ def get_output_of_command(command, *args, **kwargs):
sys.stdout = old_stdout
return output.getvalue()
def test_import_export(app, some_data):
output = get_output_of_command('export_site')
assert len(json.loads(output)['pages']) == 3
@ -89,6 +88,36 @@ def test_import_export(app, some_data):
assert os.path.exists(os.path.join(tempdir, 't.json'))
shutil.rmtree(tempdir)
def test_import_export_with_parent(app, some_data):
output = get_output_of_command('export_site')
payload = json.loads(output)
payload['pages'][1]['fields']['parent'] = ['one']
Page.objects.all().delete()
import_site(data=payload)
assert Page.objects.count() == 3
two = Page.objects.get(slug='two')
assert two.parent.slug == 'one'
def test_import_export_with_unknown_parent(app, some_data):
output = get_output_of_command('export_site')
payload = json.loads(output)
payload['pages'][0]['fields']['exclude_from_navigation'] = False
payload['pages'][0]['fields']['parent'] = ['unknown-parent']
Page.objects.all().delete()
import_site(data=payload)
assert Page.objects.count() == 3
for page in Page.objects.all():
assert page.parent is None
one = Page.objects.get(slug='one')
assert one.exclude_from_navigation is True
def test_backward_compatibility_import(app, some_data):
old_export = Page.export_all_for_json()
Page.objects.all().delete()

View File

@ -1,5 +1,6 @@
import base64
import datetime
import json
import mock
import os
import re
@ -14,6 +15,7 @@ from django.template import TemplateSyntaxError
from django.test import override_settings
from django.test.client import RequestFactory
from django.test.utils import CaptureQueriesContext
from django.utils.encoding import force_bytes, force_str
from django.utils.http import urlencode
from django.utils.timezone import now
from django.utils.six import BytesIO
@ -594,6 +596,7 @@ def test_site_export_import(app, admin_user):
resp = resp.form.submit()
assert 'File is not in the expected JSON format.' in resp.text
def test_site_export_import_missing_group(app, admin_user):
Page.objects.all().delete()
group = Group.objects.create(name='foobar')
@ -618,6 +621,24 @@ def test_site_export_import_missing_group(app, admin_user):
assert 'Missing groups: foobar' in resp.text
def test_site_export_import_unknown_parent(app, admin_user):
Page.objects.create(title='One', slug='one', template_name='standard')
Page.objects.create(title='Two', slug='two', template_name='standard')
app = login(app)
resp = app.get('/manage/')
resp = resp.click('Export Site')
payload = json.loads(force_str(resp.body))
payload['pages'][0]['fields']['exclude_from_navigation'] = False
payload['pages'][0]['fields']['parent'] = ['unknown-parent']
resp = app.get('/manage/')
resp = resp.click('Import Site')
resp.form['site_json'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
resp = resp.form.submit().follow()
assert 'Unknown parent for page "One"; parent has been reset and page was excluded from navigation.' in resp.text
def test_invalid_cell_report(app, admin_user):
app = login(app)
resp = app.get('/manage/cells/invalid-report/')