Repository: DDMAL/CantusDB

Issue:
Feast dropdown could use autocomplete 
The feast selector dropdown select on the chant edit/create page could use a searchable autocomplete widget so the user won't have to scroll through hundreds of items.


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/forms.py b/django/cantusdb_project/main_app/forms.py
--- a/django/cantusdb_project/main_app/forms.py
+++ b/django/cantusdb_project/main_app/forms.py
@@ -109,7 +109,7 @@ class Meta:
             # genre: defined below (foreignkey)
             "position": TextInputWidget(),
             "cantus_id": TextInputWidget(),
-            #'feast': defined below (foreignkey)
+            "feast": autocomplete.ModelSelect2(url="feast-autocomplete"),
             "mode": TextInputWidget(),
             "differentia": TextInputWidget(),
             "differentia_new": TextInputWidget(),
@@ -154,11 +154,6 @@ class Meta:
     )
     genre.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
-    feast = forms.ModelChoiceField(
-        queryset=Feast.objects.all().order_by("name"), required=False
-    )
-    feast.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
-
     manuscript_full_text_std_spelling = forms.CharField(
         required=True,
         widget=TextAreaWidget,
@@ -303,7 +298,7 @@ class Meta:
             "marginalia": TextInputWidget(),
             # folio: defined below (required)
             # c_sequence: defined below (required)
-            # feast: defined below (foreignkey)
+            "feast": autocomplete.ModelSelect2(url="feast-autocomplete"),
             # office: defined below (foreignkey)
             # genre: defined below (foreignkey)
             "position": TextInputWidget(),
@@ -341,11 +336,6 @@ class Meta:
         help_text="Each folio starts with '1'.",
     )
 
-    feast = forms.ModelChoiceField(
-        queryset=Feast.objects.all().order_by("name"), required=False
-    )
-    feast.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
-
     # We use NameModelChoiceField here so the dropdown list of office/mass displays the name
     # instead of [name] + description
     office = NameModelChoiceField(
diff --git a/django/cantusdb_project/main_app/urls.py b/django/cantusdb_project/main_app/urls.py
--- a/django/cantusdb_project/main_app/urls.py
+++ b/django/cantusdb_project/main_app/urls.py
@@ -68,6 +68,7 @@
     AllUsersAutocomplete,
     CenturyAutocomplete,
     RismSiglumAutocomplete,
+    FeastAutocomplete,
 )
 
 urlpatterns = [
@@ -466,6 +467,11 @@
         RismSiglumAutocomplete.as_view(),
         name="rismsiglum-autocomplete",
     ),
+    path(
+        "feast-autocomplete/",
+        FeastAutocomplete.as_view(),
+        name="feast-autocomplete",
+    ),
 ]
 
 handler404 = "main_app.views.views.handle404"
diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py
--- a/django/cantusdb_project/main_app/views/views.py
+++ b/django/cantusdb_project/main_app/views/views.py
@@ -1003,3 +1003,15 @@ def get_queryset(self):
         if self.q:
             qs = qs.filter(name__istartswith=self.q)
         return qs
+
+
+class FeastAutocomplete(autocomplete.Select2QuerySetView):
+    def get_queryset(self):
+        if not self.request.user.is_authenticated:
+            return Feast.objects.none()
+        qs = Feast.objects.all().order_by("name")
+        if self.q:
+            qs = qs.filter(
+                Q(name__istartswith=self.q) | Q(feast_code__istartswith=self.q)
+            )
+        return qs


Test patch: