From 5cad0989495e35c7246e71c6a0f1289d8274fdb0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Wed, 21 Sep 2022 16:49:05 +0200
Subject: [PATCH] manager: reduce num of queries on page view (#69400)

---
 combo/apps/wcs/models.py | 23 ++++++++++++++++-------
 combo/data/models.py     | 17 ++++++++++++++---
 combo/manager/forms.py   |  2 ++
 combo/manager/views.py   |  7 ++++++-
 combo/settings.py        |  2 +-
 tests/test_manager.py    |  4 ++--
 tests/wcs/test_card.py   |  4 ++--
 7 files changed, 43 insertions(+), 16 deletions(-)

diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index fa41d27d..1ec43426 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -33,6 +33,7 @@ from django.utils.translation import gettext_lazy as _
 from django.utils.translation import pgettext_lazy
 from requests.exceptions import RequestException
 
+from combo import utils
 from combo.data.library import register_cell_class
 from combo.data.models import CellBase, Page
 from combo.utils import NothingInCacheException, requests
@@ -1143,13 +1144,8 @@ class WcsCardCell(CardMixin, CellBase):
 
         # get cells with explicit ids
         results = []
-        cells = (
-            WcsCardCell.objects.filter(page=self.page_id)
-            .exclude(pk=self.pk)
-            .exclude(slug='')  # no slug
-            .filter(related_card_path='')  # no explicit ids
-            .exclude(card_ids__contains=',')  # multiple ids, can not follow relations
-        )
+        cells = WcsCardCell.get_cells_with_explicit_ids_by_page().get(self.page_id) or []
+        cells = [c for c in cells if c.pk != self.pk]
         many_cells = len(cells) > 1
         for cell in cells:
             # follow relations
@@ -1164,6 +1160,19 @@ class WcsCardCell(CardMixin, CellBase):
             )
         return results
 
+    @classmethod
+    @utils.cache_during_request
+    def get_cells_with_explicit_ids_by_page(cls):
+        result = collections.defaultdict(list)
+        cells = (
+            cls.objects.exclude(slug='')  # no slug
+            .filter(related_card_path='')  # no explicit ids
+            .exclude(card_ids__contains=',')  # multiple ids, can not follow relations
+        ).order_by('order')
+        for cell in cells:
+            result[cell.page_id].append(cell)
+        return result
+
     def get_card_ids_from_related(self, context, request):
         def get_relation(relations, varname, reverse):
             for relation in relations:
diff --git a/combo/data/models.py b/combo/data/models.py
index 761bc30b..f7caa5a0 100644
--- a/combo/data/models.py
+++ b/combo/data/models.py
@@ -927,14 +927,21 @@ class CellBase(models.Model, metaclass=CellMeta):
     def cleaned_extra_css_class(self):
         return ' '.join([x for x in self.extra_css_class.split() if not x.startswith('size--')])
 
-    @property
-    def asset_css_classes(self):
+    @classmethod
+    @utils.cache_during_request
+    def get_assets_by_key(cls):
         from combo.apps.assets.models import Asset
 
+        return {a.key: a for a in Asset.objects.all()}
+
+    @property
+    def asset_css_classes(self):
         if not hasattr(self, '_asset_keys'):
             self._asset_keys = self.get_asset_slot_keys()
         if not hasattr(self, '_assets'):
-            self._assets = {a.key: a for a in Asset.objects.filter(key__in=self._asset_keys.keys())}
+            all_assets = CellBase.get_assets_by_key()
+            self._assets = {key: all_assets.get(key) for key in self._asset_keys.keys()}
+            self._assets = {k: v for k, v in self._assets.items() if v}
 
         if not self._asset_keys or not self._assets:
             return ''
@@ -1023,6 +1030,7 @@ class CellBase(models.Model, metaclass=CellMeta):
         cell_filter=None,
         skip_cell_cache=False,
         prefetch_validity_info=False,
+        prefetch_groups=False,
         select_related=None,
         load_contenttypes=False,
         cells_exclude=None,
@@ -1065,6 +1073,8 @@ class CellBase(models.Model, metaclass=CellMeta):
                 cells_queryset = cells_queryset.exclude(cells_exclude)
             if extra_filter:
                 cells_queryset = cells_queryset.filter(extra_filter)
+            if prefetch_groups:
+                cells_queryset = cells_queryset.prefetch_related('groups')
             if select_related:
                 cells_queryset = cells_queryset.select_related(
                     *select_related.get('__all__', []), *select_related.get(klass.get_cell_type_str(), [])
@@ -1078,6 +1088,7 @@ class CellBase(models.Model, metaclass=CellMeta):
                     for v in validity_info_list
                     if v.object_id == cell.pk and v.content_type.model_class() == cell.__class__
                 ]
+
         cells.sort(key=lambda x: x.order)
         return cells
 
diff --git a/combo/manager/forms.py b/combo/manager/forms.py
index c6375cc8..0d2de775 100644
--- a/combo/manager/forms.py
+++ b/combo/manager/forms.py
@@ -25,12 +25,14 @@ from django.template import Template, TemplateSyntaxError
 from django.template.loader import TemplateDoesNotExist, get_template
 from django.utils.translation import gettext_lazy as _
 
+from combo import utils
 from combo.data.forms import get_page_choices
 from combo.data.models import Page, ParentContentCell, SiteSettings, compile_sub_slug
 
 from .fields import ImageIncludingSvgField
 
 
+@utils.cache_during_request
 def get_groups_as_choices():
     return [(x.id, x.name) for x in Group.objects.all().order_by('name')]
 
diff --git a/combo/manager/views.py b/combo/manager/views.py
index 1a146f5d..14003071 100644
--- a/combo/manager/views.py
+++ b/combo/manager/views.py
@@ -419,7 +419,12 @@ class PageView(ManagedPageMixin, DetailView):
 
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
-        cells = CellBase.get_cells(page=self.object, prefetch_validity_info=True)
+        cells = CellBase.get_cells(
+            page=self.object,
+            prefetch_validity_info=True,
+            prefetch_groups=True,
+            select_related={'__all__': ['page']},
+        )
         existing_cell_types = {cell.get_cell_type_str() for cell in cells}
         cell_type_groups = {}
         for cell_type in CellBase.get_all_cell_types():
diff --git a/combo/settings.py b/combo/settings.py
index 9a4ec5ad..b123728e 100644
--- a/combo/settings.py
+++ b/combo/settings.py
@@ -274,7 +274,7 @@ LINGO_NO_ONLINE_PAYMENT_REASONS = {}
 JSON_CELL_TYPES = {}
 
 # dashboard support
-COMBO_DASHBOARD_ENABLED = True
+COMBO_DASHBOARD_ENABLED = False
 
 COMBO_DASHBOARD_NEW_TILE_POSITION = 'last'
 
diff --git a/tests/test_manager.py b/tests/test_manager.py
index 736afd2c..2e86b8d1 100644
--- a/tests/test_manager.py
+++ b/tests/test_manager.py
@@ -702,7 +702,7 @@ def test_edit_page_num_queries(settings, app, admin_user):
     app.get('/manage/pages/%s/' % page.pk)  # load once to populate caches
     with CaptureQueriesContext(connection) as ctx:
         app.get('/manage/pages/%s/' % page.pk)
-    assert len(ctx.captured_queries) == 44
+    assert len(ctx.captured_queries) == 40
 
 
 def test_delete_page(app, admin_user):
@@ -2557,7 +2557,7 @@ def test_page_versionning(app, admin_user):
     resp = resp.click('restore', index=6)
     with CaptureQueriesContext(connection) as ctx:
         resp = resp.form.submit().follow()
-        assert len(ctx.captured_queries) == 153
+        assert len(ctx.captured_queries) == 147
 
     resp2 = resp.click('See online')
     assert resp2.text.index('Foobar1') < resp2.text.index('Foobar2') < resp2.text.index('Foobar3')
diff --git a/tests/wcs/test_card.py b/tests/wcs/test_card.py
index 49ab0431..b6201cfd 100644
--- a/tests/wcs/test_card.py
+++ b/tests/wcs/test_card.py
@@ -686,23 +686,23 @@ def test_manager_card_cell(mock_send, app, admin_user):
     assert resp.forms[0]['c%s-related_card_path' % cell.get_reference()].options == [
         ('__all__', False, 'All cards'),
         ('--', True, 'Card whose identifier is in the URL'),
+        ('sluge-bis/cardd-bar/carde-foo', False, 'Linked card (From cell sluge-bis): "Card D" -> "Card E"'),
         (
             'sluge-again/cardd-bar/carde-foo',
             False,
             'Linked card (From cell sluge-again): "Card D" -> "Card E"',
         ),
-        ('sluge-bis/cardd-bar/carde-foo', False, 'Linked card (From cell sluge-bis): "Card D" -> "Card E"'),
         ('', False, 'Template'),
     ]
     assert resp.forms[1]['c%s-related_card_path' % cell2.get_reference()].options == [
         ('__all__', False, 'All cards'),
         ('--', True, 'Card whose identifier is in the URL'),
+        ('sluge/cardd-bar/carde-foo', False, 'Linked card (From cell sluge): "Card D" -> "Card E"'),
         (
             'sluge-again/cardd-bar/carde-foo',
             False,
             'Linked card (From cell sluge-again): "Card D" -> "Card E"',
         ),
-        ('sluge/cardd-bar/carde-foo', False, 'Linked card (From cell sluge): "Card D" -> "Card E"'),
         ('', False, 'Template'),
     ]
 
-- 
2.35.1

