From a5239bfba9a3d30eb7ce7467b824cfee2fe1cfa1 Mon Sep 17 00:00:00 2001
From: Konrad Mohrfeldt <konrad.mohrfeldt@farbdev.org>
Date: Mon, 28 Mar 2022 23:57:22 +0200
Subject: [PATCH] =?UTF-8?q?fix:=20don=E2=80=99t=20enforce=20valid=20instan?=
 =?UTF-8?q?ce=20references=20in=20collection=20filters?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

If a collection is filtered by an invalid id (meaning the model the
query argument points to with that specific id does not exist), we no
longer return an error. Instead the filter is applied without a lookup.

Fixes #104
---
 program/filters.py | 43 +++++++++++++++----------------------------
 1 file changed, 15 insertions(+), 28 deletions(-)

diff --git a/program/filters.py b/program/filters.py
index ea43ff7b..8478b6f9 100644
--- a/program/filters.py
+++ b/program/filters.py
@@ -3,7 +3,7 @@ import datetime
 from django_filters import rest_framework as filters
 from django_filters import widgets
 
-from django.contrib.auth.models import User
+from django import forms
 from django.db.models import Q, QuerySet
 from django.utils import timezone
 from program import models
@@ -19,7 +19,7 @@ class StaticFilterHelpTextMixin:
         return _filter
 
 
-class ModelMultipleChoiceFilter(filters.ModelMultipleChoiceFilter):
+class IntegerInFilter(filters.BaseInFilter):
     class QueryArrayWidget(widgets.QueryArrayWidget):
         # see: https://github.com/carltongibson/django-filter/issues/1047
         def value_from_datadict(self, data, files, name):
@@ -31,17 +31,12 @@ class ModelMultipleChoiceFilter(filters.ModelMultipleChoiceFilter):
                     new_data[key] = data.getlist(key)
             return super().value_from_datadict(new_data, files, name)
 
+    field_class = forms.IntegerField
+
     def __init__(self, *args, **kwargs):
         kwargs.setdefault("widget", self.QueryArrayWidget())
-        kwargs["lookup_expr"] = "in"
         super().__init__(*args, **kwargs)
 
-    def get_filter_predicate(self, v):
-        # There is something wrong with using ModelMultipleChoiceFilter
-        # along the CSVWidget that causes lookups to fail.
-        # May be related to: https://github.com/carltongibson/django-filter/issues/1103
-        return super().get_filter_predicate([v.pk])
-
 
 class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
     active = filters.BooleanFilter(
@@ -52,34 +47,31 @@ class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
             "or past or upcoming shows if false."
         ),
     )
-    host = ModelMultipleChoiceFilter(
-        queryset=models.Host.objects.all(),
+    host = IntegerInFilter(
         field_name="hosts",
         help_text="Return only shows assigned to the given host(s).",
     )
     # TODO: replace `musicfocus` with `music_focus` when dashboard is updated
-    musicfocus = ModelMultipleChoiceFilter(
-        queryset=models.MusicFocus.objects.all(),
+    musicfocus = IntegerInFilter(
         field_name="music_focus",
         help_text="Return only shows with given music focus(es).",
     )
-    owner = ModelMultipleChoiceFilter(
-        queryset=User.objects.all(),
+    owner = IntegerInFilter(
         field_name="owners",
         help_text="Return only shows that belong to the given owner(s).",
     )
-    category = ModelMultipleChoiceFilter(
-        queryset=models.Category.objects.all(),
+    category = IntegerInFilter(
         help_text="Return only shows of the given category or categories.",
     )
-    language = ModelMultipleChoiceFilter(
-        queryset=models.Language.objects.all(),
+    language = IntegerInFilter(
         help_text="Return only shows of the given language(s).",
     )
-    topic = ModelMultipleChoiceFilter(
-        queryset=models.Topic.objects.all(),
+    topic = IntegerInFilter(
         help_text="Return only shows of the given topic(s).",
     )
+    type = IntegerInFilter(
+        help_text="Return only shows of a given type.",
+    )
     public = filters.BooleanFilter(
         field_name="is_public",
         help_text="Return only shows that are public/non-public.",
@@ -113,9 +105,6 @@ class ShowFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
 
     class Meta:
         model = models.Show
-        help_texts = {
-            "type": "Return only shows of a given type.",
-        }
         fields = [
             "active",
             "category",
@@ -224,14 +213,12 @@ class TimeSlotFilterSet(filters.FilterSet):
 
 
 class NoteFilterSet(StaticFilterHelpTextMixin, filters.FilterSet):
-    ids = ModelMultipleChoiceFilter(
+    ids = IntegerInFilter(
         field_name="id",
-        queryset=models.Note.objects.all(),
         help_text="Return only notes matching the specified id(s).",
     )
-    owner = ModelMultipleChoiceFilter(
+    owner = IntegerInFilter(
         field_name="show__owners",
-        queryset=models.User.objects.all(),
         help_text="Return only notes by show the specified owner(s): all notes the user may edit.",
     )
 
-- 
GitLab