Skip to content

Commit 30c1a47

Browse files
committed
Fix n+1 query on staff deleted message logs page
1 parent 1b229f6 commit 30c1a47

3 files changed

Lines changed: 25 additions & 2 deletions

File tree

pydis_site/apps/api/models/bot/user.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from functools import lru_cache
2+
13
from django.contrib.postgres.fields import ArrayField
24
from django.core.validators import MaxValueValidator, MinValueValidator
35
from django.db import models
@@ -70,14 +72,20 @@ def __str__(self):
7072
return f"{self.name}#{self.discriminator:04d}"
7173
return self.name
7274

75+
@staticmethod
76+
@lru_cache(maxsize=256)
77+
def _get_roles(role_ids: tuple[int, ...]) -> list[Role]:
78+
"""Fetch the given roles, cached to avoid refetching the same roles for every user."""
79+
return list(Role.objects.filter(id__in=role_ids))
80+
7381
@property
7482
def top_role(self) -> Role:
7583
"""
7684
Attribute that returns the user's top role.
7785
7886
This will fall back to the Developers role if the user does not have any roles.
7987
"""
80-
roles = Role.objects.filter(id__in=self.roles)
88+
roles = self._get_roles(tuple(sorted(self.roles)))
8189
if not roles:
8290
return Role.objects.get(name="Developers")
8391
return max(roles)

pydis_site/apps/api/signals.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from django.db.models.signals import post_delete
1+
from django.db.models.signals import post_delete, post_save
22
from django.dispatch import receiver
33

44
from pydis_site.apps.api.models.bot import Role, User
@@ -10,3 +10,10 @@ def delete_role_from_user(sender: Role, instance: Role, **kwargs) -> None:
1010
for user in User.objects.filter(roles__contains=[instance.id]):
1111
del user.roles[user.roles.index(instance.id)]
1212
user.save()
13+
14+
15+
@receiver(signal=post_save, sender=Role)
16+
@receiver(signal=post_delete, sender=Role)
17+
def clear_top_role_cache(sender: Role, **kwargs) -> None:
18+
"""Clear the cached role lookups used by `User.top_role` when a role changes."""
19+
User._get_roles.cache_clear()

pydis_site/apps/staff/views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from django.db.models import Prefetch
12
from django.views.generic.detail import DetailView
23

4+
from pydis_site.apps.api.models.bot.deleted_message import DeletedMessage
35
from pydis_site.apps.api.models.bot.message_deletion_context import MessageDeletionContext
46

57

@@ -9,3 +11,9 @@ class LogView(DetailView):
911
model = MessageDeletionContext
1012
context_object_name = "deletion_context"
1113
template_name = "staff/logs.html"
14+
queryset = MessageDeletionContext.objects.select_related("actor").prefetch_related(
15+
Prefetch(
16+
"deletedmessage_set",
17+
queryset=DeletedMessage.objects.select_related("author"),
18+
),
19+
)

0 commit comments

Comments
 (0)