Projet

Général

Profil

0001-import-do-not-fail-if-page.parent-is-not-found-22889.patch

Lauréline Guérin, 12 mai 2020 17:04

Télécharger (9,14 ko)

Voir les différences:

Subject: [PATCH] import: do not fail if page.parent is not found (#22889)

 combo/data/models.py        | 25 ++++++++++++----------
 combo/data/utils.py         |  4 ++--
 combo/manager/views.py      |  3 ++-
 tests/test_import_export.py | 41 +++++++++++++++++++++++++++++++------
 tests/test_manager.py       | 20 ++++++++++++++++++
 5 files changed, 73 insertions(+), 20 deletions(-)
combo/data/models.py
29 29

  
30 30
from django.apps import apps
31 31
from django.conf import settings
32
from django.contrib import messages
32 33
from django.contrib.auth.models import Group
33 34
from django.contrib.contenttypes.fields import GenericForeignKey
34 35
from django.contrib.contenttypes.fields import GenericRelation
......
399 400
        return serialized_page
400 401

  
401 402
    @classmethod
402
    def load_serialized_page(cls, json_page, snapshot=None):
403
    def load_serialized_page(cls, json_page, snapshot=None, request=None):
403 404
        json_page['model'] = 'data.page'
404 405
        json_page['fields']['groups'] = [[x] for x in json_page['fields']['groups'] if isinstance(x, six.string_types)]
405 406
        page, created = Page.objects.get_or_create(slug=json_page['fields']['slug'], snapshot=snapshot)
406 407
        json_page['pk'] = page.id
408
        parent_slug = json_page['fields'].get('parent') or []
409
        if parent_slug and not Page.objects.filter(slug=parent_slug[0]).exists():
410
            # parent not found, remove it and exclude page from navigation
411
            json_page['fields'].pop('parent')
412
            json_page['fields']['exclude_from_navigation'] = True
413
            if request:
414
                messages.warning(
415
                    request,
416
                    _('Unknown parent for page "%s"; parent has been reset and page was excluded from navigation.')
417
                    % json_page['fields']['title'])
407 418
        page = next(serializers.deserialize('json', json.dumps([json_page]), ignorenonexistent=True))
408 419
        page.object.snapshot = snapshot
409 420
        page.save()
......
414 425
            else:
415 426
                cell['fields']['page'] = page.object.natural_key()
416 427

  
417

  
418 428
        # if there were cells, remove them
419 429
        for cell in CellBase.get_cells(page_id=page.object.id):
420 430
            cell.delete()
......
433 443
            cell.object.import_subobjects(cell_data)
434 444

  
435 445
    @classmethod
436
    def load_serialized_pages(cls, json_site):
446
    def load_serialized_pages(cls, json_site, request=None):
437 447
        cells = []
438 448
        for json_page in json_site:
439
            cls.load_serialized_page(json_page)
449
            cls.load_serialized_page(json_page, request=request)
440 450
            cells.extend(json_page.get('cells'))
441 451
        cls.load_serialized_cells(cells)
442 452

  
443
        # 2nd pass to set parents
444
        for json_page in json_site:
445
            if json_page.get('parent_slug'):
446
                page = Page.objects.get(slug=json_page['fields']['slug'])
447
                page.parent = Page.objects.get(slug=json_page.get('parent_slug'))
448
                page.save()
449

  
450 453
    @classmethod
451 454
    def export_all_for_json(cls):
452 455
        ordered_pages = Page.get_as_reordered_flat_hierarchy(cls.objects.all())
combo/data/utils.py
47 47
           }
48 48

  
49 49

  
50
def import_site(data, if_empty=False, clean=False):
50
def import_site(data, if_empty=False, clean=False, request=None):
51 51
    if isinstance(data, list):
52 52
        # old export form with a list of pages, convert it to new dictionary
53 53
        # format.
......
80 80

  
81 81
        MapLayer.load_serialized_objects(data.get('map-layers') or [])
82 82
        Asset.load_serialized_objects(data.get('assets') or [])
83
        Page.load_serialized_pages(data.get('pages') or [])
83
        Page.load_serialized_pages(data.get('pages') or [], request=request)
84 84

  
85 85
        if data.get('pwa'):
86 86
            PwaSettings.load_serialized_settings(data['pwa'].get('settings'))
combo/manager/views.py
85 85
            return self.form_invalid(form)
86 86

  
87 87
        try:
88
            import_site(json_site)
88
            import_site(json_site, request=self.request)
89 89
        except MissingGroups as e:
90 90
            form.add_error('site_json', force_text(e))
91 91
            return self.form_invalid(form)
92 92

  
93 93
        return super(SiteImportView, self).form_valid(form)
94 94

  
95

  
95 96
site_import = SiteImportView.as_view()
96 97

  
97 98

  
tests/test_import_export.py
26 26

  
27 27
@pytest.fixture
28 28
def some_data():
29
    page = Page(title='One', slug='one')
30
    page.save()
31
    page = Page(title='Two', slug='two')
32
    page.save()
33
    page = Page(title='Three', slug='three')
34
    page.save()
29
    Page.objects.create(title='One', slug='one')
30
    Page.objects.create(title='Two', slug='two')
31
    page = Page.objects.create(title='Three', slug='three')
35 32
    cell = TextCell(page=page, order=0, text='hello world', placeholder='content')
36 33
    cell.save()
37 34

  
35

  
38 36
@pytest.fixture
39 37
def some_map_layers():
40 38
    MapLayer(label='Foo', slug='foo', geojson_url='http://example.net/foo/').save()
......
52 50
    sys.stdout = old_stdout
53 51
    return output.getvalue()
54 52

  
53

  
55 54
def test_import_export(app, some_data):
56 55
    output = get_output_of_command('export_site')
57 56
    assert len(json.loads(output)['pages']) == 3
......
89 88
    assert os.path.exists(os.path.join(tempdir, 't.json'))
90 89
    shutil.rmtree(tempdir)
91 90

  
91

  
92
def test_import_export_with_parent(app, some_data):
93
    output = get_output_of_command('export_site')
94
    payload = json.loads(output)
95
    payload['pages'][1]['fields']['parent'] = ['one']
96

  
97
    Page.objects.all().delete()
98
    import_site(data=payload)
99

  
100
    assert Page.objects.count() == 3
101
    two = Page.objects.get(slug='two')
102
    assert two.parent.slug == 'one'
103

  
104

  
105
def test_import_export_with_unknown_parent(app, some_data):
106
    output = get_output_of_command('export_site')
107
    payload = json.loads(output)
108
    payload['pages'][0]['fields']['exclude_from_navigation'] = False
109
    payload['pages'][0]['fields']['parent'] = ['unknown-parent']
110

  
111
    Page.objects.all().delete()
112
    import_site(data=payload)
113

  
114
    assert Page.objects.count() == 3
115
    for page in Page.objects.all():
116
        assert page.parent is None
117
    one = Page.objects.get(slug='one')
118
    assert one.exclude_from_navigation is True
119

  
120

  
92 121
def test_backward_compatibility_import(app, some_data):
93 122
    old_export = Page.export_all_for_json()
94 123
    Page.objects.all().delete()
tests/test_manager.py
15 15
from django.test import override_settings
16 16
from django.test.client import RequestFactory
17 17
from django.test.utils import CaptureQueriesContext
18
from django.utils.encoding import force_bytes, force_str
18 19
from django.utils.http import urlencode
19 20
from django.utils.six import BytesIO
20 21
from django.utils.six.moves.urllib import parse as urlparse
......
585 586
    resp = resp.form.submit()
586 587
    assert 'File is not in the expected JSON format.' in resp.text
587 588

  
589

  
588 590
def test_site_export_import_missing_group(app, admin_user):
589 591
    Page.objects.all().delete()
590 592
    group = Group.objects.create(name='foobar')
......
609 611
    assert 'Missing groups: foobar' in resp.text
610 612

  
611 613

  
614
def test_site_export_import_unknown_parent(app, admin_user):
615
    Page.objects.create(title='One', slug='one', template_name='standard')
616
    Page.objects.create(title='Two', slug='two', template_name='standard')
617

  
618
    app = login(app)
619
    resp = app.get('/manage/')
620
    resp = resp.click('Export Site')
621
    payload = json.loads(force_str(resp.body))
622
    payload['pages'][0]['fields']['exclude_from_navigation'] = False
623
    payload['pages'][0]['fields']['parent'] = ['unknown-parent']
624

  
625
    resp = app.get('/manage/')
626
    resp = resp.click('Import Site')
627
    resp.form['site_json'] = Upload('site-export.json', force_bytes(json.dumps(payload)), 'application/json')
628
    resp = resp.form.submit().follow()
629
    assert 'Unknown parent for page "One"; parent has been reset and page was excluded from navigation.' in resp.text
630

  
631

  
612 632
def test_invalid_cell_report(app, admin_user):
613 633
    app = login(app)
614 634
    resp = app.get('/manage/cells/invalid-report/')
615
-