From 35d9a1bbba7135f1f32d9d3e362460c027eb2fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sat, 2 Jun 2018 15:29:16 +0200 Subject: [PATCH] general: create page hierarchy beforehand (#24238) --- combo/apps/momo/utils.py | 17 +++++------------ combo/data/models.py | 32 +++++++++++++++++++++++++++----- combo/public/menu.py | 17 +++++++++++++---- tests/test_public.py | 7 ++++--- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/combo/apps/momo/utils.py b/combo/apps/momo/utils.py index f76bfa77..abb25f18 100644 --- a/combo/apps/momo/utils.py +++ b/combo/apps/momo/utils.py @@ -95,8 +95,8 @@ def get_page_dict(request, page, manifest): if page_dict.get('external') and icon_cells[0].embed_page: page_dict['external'] = False - if hasattr(page, '_children') and page._children: - children = page._children + if page.slug == 'index' and page.parent_id is None: # home + children = page.get_siblings()[1:] else: children = page.get_children() @@ -143,18 +143,11 @@ def generate_manifest(request): # - the application pages are created from the homepage siblings and their # children # - the application menu is created from direct children of the homepage - children = [] - homepage = None - for page in level0_pages: - if page.slug == 'index': - homepage = page - else: - children.append(page) - - if not homepage: + try: + homepage = Page.objects.get(slug='index', parent_id=None) + except Page.DoesNotExist: raise GenerationError(_('The homepage needs to be created first.')) - homepage._children = children manifest.update(get_page_dict(request, homepage, manifest)) # footer diff --git a/combo/data/models.py b/combo/data/models.py index f48c2f53..03731b6d 100644 --- a/combo/data/models.py +++ b/combo/data/models.py @@ -145,7 +145,6 @@ class Page(models.Model): related_cells = JSONField(blank=True) _level = None - _children = None class Meta: ordering = ['order'] @@ -185,7 +184,7 @@ class Page(models.Model): pages = [self] page = self while page.parent_id: - page = page.parent + page = page._parent if hasattr(page, '_parent') else page.parent pages.append(page) return list(reversed(pages)) @@ -202,7 +201,7 @@ class Page(models.Model): parts = [self] page = self while page.parent_id: - page = page.parent + page = page._parent if hasattr(page, '_parent') else page.parent parts.append(page) parts.reverse() try: @@ -211,12 +210,19 @@ class Page(models.Model): return None def get_siblings(self): - return Page.objects.filter(parent=self.parent) + if hasattr(self, '_parent'): + if self._parent: + return self._parent._children + return Page.objects.filter(parent_id=self.parent_id) def get_children(self): + if hasattr(self, '_children'): + return self._children return Page.objects.filter(parent_id=self.id) def has_children(self): + if hasattr(self, '_children'): + return bool(self._children) return Page.objects.filter(parent_id=self.id).exists() def get_template_display_name(self): @@ -282,7 +288,6 @@ class Page(models.Model): parenting[page.parent_id].append(page) def fill_list(object_sublist, level=0, parent=None): for page in object_sublist: - page._children = parenting.get(page.id) if page.parent == parent: page.level = level reordered.append(page) @@ -291,6 +296,23 @@ class Page(models.Model): fill_list(object_list) return reordered + @staticmethod + @utils.cache_during_request + def get_with_hierarchy_attributes(): + pages = Page.objects.all() + pages_by_id = {} + for page in pages: + pages_by_id[page.id] = page + page._parent = None + page._children = [] + for page in pages: + page._parent = pages_by_id[page.parent_id] if page.parent_id else None + if page._parent: + page._parent._children.append(page) + for page in pages: + page._children.sort(key=lambda x: x.order) + return pages_by_id + def visibility(self): if self.public: return _('Public') diff --git a/combo/public/menu.py b/combo/public/menu.py index d15a3fc9..db6df724 100644 --- a/combo/public/menu.py +++ b/combo/public/menu.py @@ -16,6 +16,9 @@ from django.template.loader import get_template +from combo.data.models import Page + + def render_menu(context, level=0, root_page=None, depth=1): context['root_page'] = root_page if root_page: @@ -25,17 +28,23 @@ def render_menu(context, level=0, root_page=None, depth=1): return template.render(context) def get_menu_context(context, level=0, current_page=None, depth=1): + if 'hierarchical_pages_by_id' not in context: + context['hierarchical_pages_by_id'] = Page.get_with_hierarchy_attributes() + pages_by_id = context['hierarchical_pages_by_id'] context['depth'] = depth if current_page is None: - current_page = context['page'] + current_page = pages_by_id.get(context['page'].id, context['page']) + else: + current_page = pages_by_id.get(current_page.id, current_page) if 'root_page' in context: + root_page = pages_by_id[context['root_page'].id] if context.get('root_page') else None # if the menu is anchored at a specific root page, we make sure the # current page is in the right path, otherwise we fall back to using # the root page as base. ariane = current_page.get_parents_and_self() - if not context.get('root_page') in ariane: - page_of_level = context['root_page'] + if not root_page in ariane: + page_of_level = root_page else: page_of_level = current_page.get_page_of_level(level) else: @@ -51,7 +60,7 @@ def get_menu_context(context, level=0, current_page=None, depth=1): context['menuitems'] = [] return context else: - if page_of_level is context.get('root_page'): + if page_of_level == context.get('root_page'): elements = page_of_level.get_children() else: elements = page_of_level.get_siblings() diff --git a/tests/test_public.py b/tests/test_public.py index 595145bb..2eea953b 100644 --- a/tests/test_public.py +++ b/tests/test_public.py @@ -103,20 +103,21 @@ def test_page_footer_acquisition(app): page5.save() ParentContentCell(page=page5, placeholder='footer', order=0).save() - # check growth in SQL queries is limited to an additional page lookup + # check growth in SQL queries is limited with CaptureQueriesContext(connection) as ctx: resp = app.get('/second/third/', status=200) assert resp.body.count('BARFOO') == 1 assert resp.body.count('BAR2FOO') == 1 queries_count_third = len(ctx.captured_queries) - assert queries_count_third == queries_count_second + 1 + assert queries_count_third == queries_count_second with CaptureQueriesContext(connection) as ctx: resp = app.get('/second/third/fourth/', status=200) assert resp.body.count('BARFOO') == 1 assert resp.body.count('BAR2FOO') == 1 queries_count_fourth = len(ctx.captured_queries) - assert queries_count_fourth == queries_count_second + 2 + # +1 for get_parents_and_self() + assert queries_count_fourth == queries_count_second + 1 # check footer doesn't get duplicated in real index children page6 = Page(title='Sixth', slug='sixth', template_name='standard', parent=page_index)