From 435e72d5f73532d93e2406e71d28e97b4013a2be Mon Sep 17 00:00:00 2001
From: Konrad Mohrfeldt <konrad.mohrfeldt@farbdev.org>
Date: Thu, 20 Mar 2025 17:27:56 +0100
Subject: [PATCH] fix: return deterministic result set for episode
 has_timeslots filter
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

For some reason the episode result set was unstable when using the
has_timeslots filter. Additionally, the former solution always added a
join to the query, even if the the has_timeslots filter wasn’t used.
---
 program/filters.py | 3 ++-
 program/views.py   | 3 +--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/program/filters.py b/program/filters.py
index 0ae9512..f530bf1 100644
--- a/program/filters.py
+++ b/program/filters.py
@@ -299,7 +299,8 @@ class EpisodeFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
         field_name="timeslots",
         help_text="Return only episodes that belong to the specified timeslot(s).",
     )
-    has_timeslots = filters.BooleanFilter(
+    has_timeslots = StaticQueryBooleanFilter(
+        query=Exists(models.TimeSlot.objects.filter(episode=OuterRef("pk"))),
         label="Has timeslots",
         help_text="Returns only timeslots that either have or have not any timeslots.",
     )
diff --git a/program/views.py b/program/views.py
index e01476a..cc84c93 100644
--- a/program/views.py
+++ b/program/views.py
@@ -46,7 +46,6 @@ from rest_framework.response import Response
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.db import IntegrityError
-from django.db.models import Q
 from django.http import HttpResponseRedirect, JsonResponse
 from django.shortcuts import get_object_or_404
 from django.utils import timezone
@@ -1449,7 +1448,7 @@ class EpisodeOwnershipPermission(ShowOwnershipPermission):
     destroy=extend_schema(summary="Delete an existing episode."),
 )
 class APIEpisodeViewSet(viewsets.ModelViewSet):
-    queryset = Episode.objects.all().annotate(has_timeslots=Q(timeslots__isnull=True)).distinct()
+    queryset = Episode.objects.all()
     pagination_class = LimitOffsetPagination
     serializer_class = EpisodeSerializer
     filter_backends = [DjangoFilterBackend, drf_filters.SearchFilter]
-- 
GitLab