From ab826b295dfb5fda8bc771c617957a02aa0364cd Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Wed, 5 Jun 2024 18:31:22 +0200
Subject: [PATCH] Avoid unnecessary queries to fetch related data

---
 aleksis/apps/alsijil/managers.py             | 26 +++++++++++++++++++-
 aleksis/apps/alsijil/models.py               |  8 +++++-
 aleksis/apps/alsijil/schema/documentation.py |  2 +-
 3 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/aleksis/apps/alsijil/managers.py b/aleksis/apps/alsijil/managers.py
index 2e8b2e558..7d0130805 100644
--- a/aleksis/apps/alsijil/managers.py
+++ b/aleksis/apps/alsijil/managers.py
@@ -11,7 +11,7 @@ from django.utils.translation import gettext as _
 from calendarweek import CalendarWeek
 
 from aleksis.apps.chronos.managers import DateRangeQuerySetMixin
-from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations
+from aleksis.core.managers import AlekSISBaseManagerWithoutMigrations, PolymorphicBaseManager
 
 if TYPE_CHECKING:
     from aleksis.core.models import Group
@@ -187,3 +187,27 @@ class GroupRoleAssignmentQuerySet(DateRangeQuerySetMixin, QuerySet):
     def for_group(self, group: "Group"):
         """Filter all role assignments for a group."""
         return self.filter(Q(groups=group) | Q(groups__child_groups=group))
+
+
+class DocumentationManager(PolymorphicBaseManager):
+    """Manager adding specific methods to documentations."""
+
+    def get_queryset(self):
+        """Ensure often used related data are loaded as well."""
+        return (
+            super()
+            .get_queryset()
+            .select_related(
+                "course",
+                "subject",
+            )
+            .prefetch_related("teachers")
+        )
+
+
+class ParticipationStatusManager(PolymorphicBaseManager):
+    """Manager adding specific methods to participation statuses."""
+
+    def get_queryset(self):
+        """Ensure often used related data are loaded as well."""
+        return super().get_queryset().select_related("person", "absence_reason", "base_absence")
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 95360559b..feaba143c 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -24,12 +24,14 @@ from aleksis.apps.alsijil.data_checks import (
     PersonalNoteOnHolidaysDataCheck,
 )
 from aleksis.apps.alsijil.managers import (
+    DocumentationManager,
     GroupRoleAssignmentManager,
     GroupRoleAssignmentQuerySet,
     GroupRoleManager,
     GroupRoleQuerySet,
     LessonDocumentationManager,
     LessonDocumentationQuerySet,
+    ParticipationStatusManager,
     PersonalNoteManager,
     PersonalNoteQuerySet,
 )
@@ -460,6 +462,8 @@ class Documentation(CalendarEvent):
 
     # FIXME: DataCheck
 
+    objects = DocumentationManager()
+
     course = models.ForeignKey(
         Course,
         models.PROTECT,
@@ -727,6 +731,8 @@ class ParticipationStatus(CalendarEvent):
 
     # FIXME: DataChecks
 
+    objects = ParticipationStatusManager()
+
     person = models.ForeignKey(
         "core.Person", models.CASCADE, related_name="participations", verbose_name=_("Person")
     )
@@ -760,7 +766,7 @@ class ParticipationStatus(CalendarEvent):
     )
 
     def __str__(self) -> str:
-        return f"{self.related_documentation}, {self.person}"
+        return f"{self.related_documentation.id}, {self.person}"
 
     class Meta:
         verbose_name = _("Participation Status")
diff --git a/aleksis/apps/alsijil/schema/documentation.py b/aleksis/apps/alsijil/schema/documentation.py
index 433cde4e7..82eccbbea 100644
--- a/aleksis/apps/alsijil/schema/documentation.py
+++ b/aleksis/apps/alsijil/schema/documentation.py
@@ -78,7 +78,7 @@ class DocumentationType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectTyp
         # A dummy documentation will not have any participations
         if str(root.pk).startswith("DUMMY") or not hasattr(root, "participations"):
             return []
-        return root.participations.all()
+        return root.participations.select_related("absence_reason", "base_absence").all()
 
 
 class DocumentationInputType(graphene.InputObjectType):
-- 
GitLab