From 465465c8d1d0af7bfde84a410d82782fdaf41d92 Mon Sep 17 00:00:00 2001 From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com> Date: Tue, 11 Aug 2026 18:04:11 -0500 Subject: [PATCH] Add addon__github_account to select related and cache active Add-Ons in AddOnSerializer --- documentcloud/addons/serializers.py | 11 +++++++---- documentcloud/addons/tests/test_views.py | 17 +++++++++++++++++ documentcloud/addons/views.py | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/documentcloud/addons/serializers.py b/documentcloud/addons/serializers.py index 7b02ebb9..e46c6750 100644 --- a/documentcloud/addons/serializers.py +++ b/documentcloud/addons/serializers.py @@ -68,16 +68,19 @@ def __init__(self, *args, **kwargs): @extend_schema_field(serializers.BooleanField()) def get_active(self, obj): - if hasattr(obj, "active"): # pre calculate active for efficiency return obj.active - request = self.context.get("request") if not request: return False - - return request.user.active_addons.filter(pk=obj.pk).exists() + if not hasattr(self, "_active_pks"): + # build the pinned-addon PK set once per serializer (one query for the page) + # instead of an EXISTS per addon. + self._active_pks = set( + request.user.active_addons.values_list("pk", flat=True) + ) + return obj.pk in self._active_pks class Meta: model = AddOn diff --git a/documentcloud/addons/tests/test_views.py b/documentcloud/addons/tests/test_views.py index 232ee91d..49d54710 100644 --- a/documentcloud/addons/tests/test_views.py +++ b/documentcloud/addons/tests/test_views.py @@ -351,6 +351,23 @@ def test_filter_domain_no_partial_host_match(self, client): assert response.status_code == status.HTTP_200_OK assert response.json()["results"] == [] + def test_list_expand_query_count(self, client, django_assert_num_queries): + """ + Expanding addon+event must not scale queries with the number of runs. + Query count stays flat (the run's addon/github_account/event are + select_related'd and get_active reads a cached PK set). + """ + user = UserFactory(is_staff=True) + client.force_authenticate(user=user) + url = "/api/addon_runs/?expand=addon,event&per_page=100" + + for expected_count in range(1, 11): + AddOnRunFactory(user=user, addon=AddOnFactory()) + with django_assert_num_queries(7): + response = client.get(url) + assert response.status_code == status.HTTP_200_OK + assert len(response.json()["results"]) == expected_count + @pytest.mark.django_db() class TestAddOnEventAPI: diff --git a/documentcloud/addons/views.py b/documentcloud/addons/views.py index eb76e8dc..9fac6fe1 100644 --- a/documentcloud/addons/views.py +++ b/documentcloud/addons/views.py @@ -729,7 +729,7 @@ def get_queryset(self): """Only fetch add-on runs viewable to this user""" queryset = AddOnRun.objects.get_viewable(self.request.user) if is_expanded(self.request, "addon"): - queryset = queryset.select_related("addon") + queryset = queryset.select_related("addon", "addon__github_account") if is_expanded(self.request, "event"): queryset = queryset.select_related("event") return queryset