Repository: netbox-community/netbox
Pull request: 16256 - Allow alphabetical ordering of bookmarks on dashboard
### Fixes: #16256

Added alphabetical ordering option for bookmarks.

Problem statement:
Allow alphabetical ordering of bookmarks on dashboard
### NetBox version

v4.0.2

### Feature type

Change to existing functionality

### Proposed functionality

Add additional order by options for bookmarks on the dashboard. 

### Use case

Alphabetical ordering is useful, and also matches the default table ordering of other sections.

### Database changes

_No response_

### External dependencies

_No response_

Hints from issue discussion:
> Add additional order by options for bookmarks on the dashboard.

Please expand on your proposal. How would bookmark ordering be specified? Are you asking that the widget itself be extended?
I didn't think I'd need to expand, sorry if I wasn't clear.

I am referencing the bookmarks widget on the dashboard, within which the configuration for it there is an 'Order by' field, the only two options are newest and oldest. I am suggesting that there be a third option called 'Alphabetical' which does what it means... sorts the bookmarks alphabetically.

![image](https://github.com/netbox-community/netbox/assets/60691199/27bca0f3-b25a-4bef-a26e-d6e6cb08ff4c)



Unfortunately alphabetical ordering is not feasible as there's no way to reliably and efficiently order by the name of generic related objects. (Not all objects have a `name` field.)
Understood.

It wouldn't be clean, but still possible by just checking if each object has a `name` field, if yes add it to an array and sort alphabetically once loop is finished. Then append the non `name` objects to the end. 
It could _probably_ be done in memory on the string representations of each object, given that the list of bookmarks typically shouldn't be very long. I'll open this for any volunteers.
Could you please assign it to me?

Commit message: Added alphabetical ordering of bookmarks.

Patch to review:
diff --git a/netbox/extras/choices.py b/netbox/extras/choices.py
index 2c9d5836a6f..514df72f8f8 100644
--- a/netbox/extras/choices.py
+++ b/netbox/extras/choices.py
@@ -117,10 +117,12 @@ class BookmarkOrderingChoices(ChoiceSet):
 
     ORDERING_NEWEST = '-created'
     ORDERING_OLDEST = 'created'
+    ORDERING_NAME_AZ = 'name'
 
     CHOICES = (
         (ORDERING_NEWEST, _('Newest')),
         (ORDERING_OLDEST, _('Oldest')),
+        (ORDERING_NAME_AZ, _('Name (A-Z)')),
     )
 
 #
diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py
index add81a318de..5d12c27be32 100644
--- a/netbox/extras/dashboard/widgets.py
+++ b/netbox/extras/dashboard/widgets.py
@@ -381,7 +381,12 @@ def render(self, request):
         if request.user.is_anonymous:
             bookmarks = list()
         else:
-            bookmarks = Bookmark.objects.filter(user=request.user).order_by(self.config['order_by'])
+            if self.config['order_by'] == BookmarkOrderingChoices.ORDERING_NAME_AZ:
+                bookmarks = sorted(Bookmark.objects.filter(user=request.user),
+                                   key=lambda bookmark: bookmark.__str__().lower()
+                                   )
+            else:
+                bookmarks = Bookmark.objects.filter(user=request.user).order_by(self.config['order_by'])
             if object_types := self.config.get('object_types'):
                 models = get_models_from_content_types(object_types)
                 conent_types = ObjectType.objects.get_for_models(*models).values()


Code review comments:
diff_hunk: @@ -117,10 +117,12 @@ class BookmarkOrderingChoices(ChoiceSet):
 
     ORDERING_NEWEST = '-created'
     ORDERING_OLDEST = 'created'
+    ORDERING_NAME_AZ = 'name'
 
     CHOICES = (
         (ORDERING_NEWEST, _('Newest')),
         (ORDERING_OLDEST, _('Oldest')),
+        (ORDERING_NAME_AZ, _('Name (A-Z)')),
path: netbox/extras/choices.py
text: @user1:
This ordering isn't technically by name, as not all model have a `name` attribute (circuits, for instance). Let's change this to "alphabetical" to be more accurate.

diff_hunk: @@ -381,7 +381,12 @@ def render(self, request):
         if request.user.is_anonymous:
             bookmarks = list()
         else:
-            bookmarks = Bookmark.objects.filter(user=request.user).order_by(self.config['order_by'])
+            if self.config['order_by'] == BookmarkOrderingChoices.ORDERING_NAME_AZ:
+                bookmarks = sorted(Bookmark.objects.filter(user=request.user),
path: netbox/extras/dashboard/widgets.py
text: @user1:
We should define the base queryset (`Bookmark.objects.filter(user=request.user)`) once, and then effect ordering one way or the other depending on the selected option. This ensures that any future modifications to the base queryset are needed only once.

diff_hunk: @@ -117,10 +117,12 @@ class BookmarkOrderingChoices(ChoiceSet):
 
     ORDERING_NEWEST = '-created'
     ORDERING_OLDEST = 'created'
+    ORDERING_NAME_AZ = 'name'
 
     CHOICES = (
         (ORDERING_NEWEST, _('Newest')),
         (ORDERING_OLDEST, _('Oldest')),
+        (ORDERING_NAME_AZ, _('Name (A-Z)')),
path: netbox/extras/choices.py
text: @user1:
I'm not sure how useful it would be, but it feels like we should add an option for reverse alphabetical ordering as well for completeness.