From 7ef26937bd6e8bb07b023528a338a85378887ec8 Mon Sep 17 00:00:00 2001
From: Konrad Mohrfeldt <konrad.mohrfeldt@farbdev.org>
Date: Tue, 12 Sep 2023 12:44:42 +0200
Subject: [PATCH] fix(REST): fix duplicate results in show result set

The filter annotation for ordering by show ownership introduced
a join that caused some results to appear more than once.
---
 program/filters.py | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/program/filters.py b/program/filters.py
index b588795a..6b645a10 100644
--- a/program/filters.py
+++ b/program/filters.py
@@ -1,10 +1,11 @@
 import datetime
 
+from django_filters import constants
 from django_filters import rest_framework as filters
 from django_filters import widgets
 
 from django import forms
-from django.db.models import Q, QuerySet
+from django.db.models import Exists, OuterRef, Q, QuerySet
 from django.utils import timezone
 from program import models
 
@@ -38,9 +39,25 @@ class IntegerInFilter(filters.BaseInFilter):
         super().__init__(*args, **kwargs)
 
 
+class ShowOrderingFilter(filters.OrderingFilter):
+    def filter(self, qs: QuerySet, value):
+        if value in constants.EMPTY_VALUES:
+            return qs
+        ordering = [self.get_ordering_value(param) for param in value]
+        fields = (field.lstrip("-") for field in ordering)
+        if "is_owner" in fields:
+            _id = getattr(self.parent.request.user, "id", None)
+            qs = qs.annotate(
+                is_owner=Exists(
+                    models.Show.owners.through.objects.filter(user=_id, show=OuterRef("pk")),
+                )
+            )
+        return qs.order_by(*ordering)
+
+
 class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
-    order = filters.OrderingFilter(
-        fields=["name", "id", "is_active", "is_owner"],
+    order = ShowOrderingFilter(
+        fields=["name", "slug", "id", "is_active", "is_owner"],
         help_text="Order shows by the given field(s).",
     )
     category_ids = IntegerInFilter(
@@ -100,11 +117,6 @@ class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
         help_text="Return only shows of the given type slug.",
     )
 
-    def filter_queryset(self, queryset):
-        _id = getattr(self.request.user, "id", None)
-        queryset = queryset.annotate(is_owner=Q(owners=_id))
-        return super().filter_queryset(queryset)
-
     def filter_active(self, queryset: QuerySet, name: str, value: bool):
         # Filter currently running shows
         # Get currently running schedules to filter by first
-- 
GitLab