Repository: DDMAL/CantusDB

Issue:
Internal string representations of objects shown in Office, Genre dropdowns on chant-edit, source-edit-chants pages
found on http://206.12.88.113/edit-chants/123610?pk=243158&folio=001v

![Screen Shot 2023-01-11 at 13 18 15](https://user-images.githubusercontent.com/58090591/211886262-5dd19131-d819-4be1-b5cb-47825fed2228.png)



Discussion:
not clear to me what the problem is--are you saying these should be V and A and not "[V] First Vespers"?
Basically, yes.

The issue is that every model in our app has a defined string representation, which is mostly used to unambiguously identify objects for the admin panel, development/debugging, etc. If ever in the future we decide to rework some feature having to do with offices or genres, it might be useful to have offices' IDs, say, displayed while interacting with them in the django shell. These are only really meant to be used internally, but tweaking the string representation would have the unintended side effect of changing what's displayed in the dropdown on this page.

The dropdowns can include the full-spelled out versions "first vespers" and "antiphon", sure. The point is that we should decide what we want to be be displayed in the dropdowns, and explicitly set it, to prevent unintended side effects.
cf. #699, on Chant Create page

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
@@ -45,6 +45,20 @@ def __init__(self, *args, **kwargs):
 """
 
 
+class NameModelChoiceField(forms.ModelChoiceField):
+    """
+    A custom ModelChoiceField that overrides the label_from_instance method
+    to display the object's name attribute instead of str(object).
+    This field is specifically designed for handling genre and office objects.
+    Rather than displaying the name along with its description, sometimes we
+    only want the shorthand notation for the genre and office objects.
+    (Eg. [AV] Antiphon verse --> AV)
+    """
+
+    def label_from_instance(self, obj):
+        return obj.name
+
+
 # 3 best
 class ChantCreateForm(forms.ModelForm):
     class Meta:
@@ -115,13 +129,19 @@ class Meta:
         help_text="Each folio starts with '1'.",
     )
 
-    office = forms.ModelChoiceField(
-        queryset=Office.objects.all().order_by("name"), required=False
+    # We use NameModelChoiceField here so the dropdown list of office/mass displays the name
+    # instead of [name] + description
+    office = NameModelChoiceField(
+        queryset=Office.objects.all().order_by("name"),
+        required=False,
     )
     office.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
-    genre = forms.ModelChoiceField(
-        queryset=Genre.objects.all().order_by("name"), required=False
+    # We use NameModelChoiceField here so the dropdown list of genres displays the name
+    # instead of [name] + description
+    genre = NameModelChoiceField(
+        queryset=Genre.objects.all().order_by("name"),
+        required=False,
     )
     genre.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
@@ -329,13 +349,19 @@ class Meta:
     )
     feast.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
-    office = forms.ModelChoiceField(
-        queryset=Office.objects.all().order_by("name"), required=False
+    # We use NameModelChoiceField here so the dropdown list of office/mass displays the name
+    # instead of [name] + description
+    office = NameModelChoiceField(
+        queryset=Office.objects.all().order_by("name"),
+        required=False,
     )
     office.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
-    genre = forms.ModelChoiceField(
-        queryset=Genre.objects.all().order_by("name"), required=False
+    # We use NameModelChoiceField here so the dropdown list of genres displays the name
+    # instead of [name] + description
+    genre = NameModelChoiceField(
+        queryset=Genre.objects.all().order_by("name"),
+        required=False,
     )
     genre.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
@@ -430,13 +456,17 @@ class Meta:
     )
     feast.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
-    office = forms.ModelChoiceField(
+    # We use NameModelChoiceField here so the dropdown list of office/mass displays the name
+    # instead of [name] + description
+    office = NameModelChoiceField(
         queryset=Office.objects.all().order_by("name"),
         required=False,
     )
     office.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
-    genre = forms.ModelChoiceField(
+    # We use NameModelChoiceField here so the dropdown list of genres displays the name
+    # instead of [name] + description
+    genre = NameModelChoiceField(
         queryset=Genre.objects.all().order_by("name"), required=False
     )
     genre.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
@@ -594,7 +624,9 @@ class Meta:
             "image_link": TextInputWidget(),
         }
 
-    genre = forms.ModelChoiceField(
+    # We use NameModelChoiceField here so the dropdown list of genres displays the name
+    # instead of [name] + description
+    genre = NameModelChoiceField(
         queryset=Genre.objects.all().order_by("name"), required=False
     )
     genre.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})


Test patch: