diff --git a/CHANGELOG.md b/CHANGELOG.md index 00725968..1556865b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Update API to allow specifying channels by names (#440) - When saving a phone number, remove any invisible unicode characers - Update the read-only dashboard's CSS for better mobile support (#442) +- Reduce the number of SQL queries used in the "Get Checks" API call ## v1.17.0 - 2020-10-14 diff --git a/hc/api/models.py b/hc/api/models.py index 8d22efec..52d4a562 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -205,8 +205,10 @@ class Check(models.Model): def channels_str(self): """ Return a comma-separated string of assigned channel codes. """ - codes = self.channel_set.order_by("code").values_list("code", flat=True) - return ",".join(map(str, codes)) + # self.channel_set may already be prefetched. + # Sort in python to make sure we do't run additional queries + codes = [str(channel.code) for channel in self.channel_set.all()] + return ",".join(sorted(codes)) @property def unique_key(self): diff --git a/hc/api/tests/test_list_checks.py b/hc/api/tests/test_list_checks.py index c887e267..e568cde0 100644 --- a/hc/api/tests/test_list_checks.py +++ b/hc/api/tests/test_list_checks.py @@ -37,7 +37,13 @@ class ListChecksTestCase(BaseTestCase): return self.client.get("/api/v1/checks/", HTTP_X_API_KEY="X" * 32) def test_it_works(self): - r = self.get() + # Expect 3 queries: + # * check API key + # * retrieve checks + # * retrieve channel codes + with self.assertNumQueries(3): + r = self.get() + self.assertEqual(r.status_code, 200) self.assertEqual(r["Access-Control-Allow-Origin"], "*") @@ -148,7 +154,10 @@ class ListChecksTestCase(BaseTestCase): self.project.api_key_readonly = "R" * 32 self.project.save() - r = self.client.get("/api/v1/checks/", HTTP_X_API_KEY="R" * 32) + # Expect a query to check the API key, and a query to retrieve checks + with self.assertNumQueries(2): + r = self.client.get("/api/v1/checks/", HTTP_X_API_KEY="R" * 32) + self.assertEqual(r.status_code, 200) # When using readonly keys, the ping URLs should not be exposed: diff --git a/hc/api/views.py b/hc/api/views.py index 13be6f4b..b2ad28c7 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -162,7 +162,8 @@ def _update(check, spec): @authorize_read def get_checks(request): q = Check.objects.filter(project=request.project) - q = q.prefetch_related("channel_set") + if not request.readonly: + q = q.prefetch_related("channel_set") tags = set(request.GET.getlist("tag")) for tag in tags: