From ab5ba1b5450c3bdf65c83b36ca09a75c6024a032 Mon Sep 17 00:00:00 2001 From: Jeroen Castelein Date: Tue, 24 Dec 2019 10:02:37 +0100 Subject: [PATCH] OPT: Support filtering on reverse related fields --- katka/views.py | 9 ++++++++- katka/viewsets.py | 12 +++++------- tests/integration/test_team_view.py | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/katka/views.py b/katka/views.py index 2d1a08f..8dbc119 100644 --- a/katka/views.py +++ b/katka/views.py @@ -51,6 +51,7 @@ class TeamViewSet(FilterViewMixin, AuditViewSet): serializer_class = TeamSerializer lookup_field = "public_identifier" lookup_value_regex = "[0-9a-f-]{36}" + parameter_lookup_map = {"application": "project__application"} def get_queryset(self): # Only show teams that are linked to a group that the user is part of @@ -256,7 +257,13 @@ class SCMReleaseViewSet(FilterViewMixin, ReadOnlyAuditViewMixin): def get_queryset(self): user_groups = self.request.user.groups.all() - return super().get_queryset().filter(scm_pipeline_runs__application__project__team__group__in=user_groups) + # Do select distinct because of the many to many relationship + return ( + super() + .get_queryset() + .distinct() + .filter(scm_pipeline_runs__application__project__team__group__in=user_groups) + ) class ApplicationMetadataViewSet(AuditViewSet): diff --git a/katka/viewsets.py b/katka/viewsets.py index 5d58c58..739e84f 100644 --- a/katka/viewsets.py +++ b/katka/viewsets.py @@ -42,13 +42,10 @@ class FilterViewMixin: def get_queryset(self): queryset = super().get_queryset() - # Fetch distinct for all model fields, to prevent duplications due to SQL Joins - queryset = queryset.distinct() - # Allow filtering on any field in serializer - all_fields = self.serializer_class.Meta.fields - filter_fields_lookup = {field: field for field in all_fields} - filter_fields_keys = list(all_fields) + all_fields = self.model._meta.get_fields() + filter_fields_lookup = {field.name: field.name for field in all_fields} + filter_fields_keys = list(field.name for field in all_fields) # Also support a mapping from query parameter to django query field if self.parameter_lookup_map: @@ -64,6 +61,7 @@ def get_queryset(self): filters[django_lookup_field] = value if filters: - queryset = queryset.filter(**filters) + # Fetch distinct for all model fields, to prevent duplications due to SQL Joins + queryset = queryset.distinct().filter(**filters) return queryset diff --git a/tests/integration/test_team_view.py b/tests/integration/test_team_view.py index 21229a3..de8931d 100644 --- a/tests/integration/test_team_view.py +++ b/tests/integration/test_team_view.py @@ -88,6 +88,30 @@ def test_get(self, client, logged_in_user, team): assert parsed["group"] == "group1" UUID(parsed["public_identifier"]) # should not raise + def test_get_by_project(self, client, logged_in_user, team, project): + response = client.get(f"/teams/{team.public_identifier}/?project={project.public_identifier}") + assert response.status_code == 200 + parsed = response.json() + assert parsed["name"] == "A-Team" + assert parsed["group"] == "group1" + UUID(parsed["public_identifier"]) # should not raise + + def test_get_by_project_bad(self, client, logged_in_user, team, project): + response = client.get(f"/teams/{team.public_identifier}/?project=12345") + assert response.status_code == 404 + + def test_get_by_application(self, client, logged_in_user, team, application): + response = client.get(f"/teams/{team.public_identifier}/?application={application.public_identifier}") + assert response.status_code == 200 + parsed = response.json() + assert parsed["name"] == "A-Team" + assert parsed["group"] == "group1" + UUID(parsed["public_identifier"]) # should not raise + + def test_get_by_application_bad(self, client, logged_in_user, team, application): + response = client.get(f"/teams/{team.public_identifier}/?application=12345") + assert response.status_code == 404 + def test_get_excludes_inactive(self, client, logged_in_user, deactivated_team): response = client.get(f"/teams/{deactivated_team.public_identifier}/") assert response.status_code == 404