diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index 45476384c..649510239 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -12,7 +12,7 @@ from rest_framework.viewsets import GenericViewSet from utilities.api import get_annotations_for_serializer, get_prefetches_for_serializer from utilities.exceptions import AbortRequest -from utilities.mptt import TreeManager +from utilities.query import reapply_model_ordering from . import mixins __all__ = ( @@ -123,16 +123,8 @@ class NetBoxModelViewSet( return obj def get_queryset(self): - """ - Reapply model-level ordering in case it has been lost through .annotate(). - https://code.djangoproject.com/ticket/32811 - """ qs = super().get_queryset() - # MPTT-based models are exempt from this; use caution when annotating querysets of these models - if any(isinstance(manager, TreeManager) for manager in qs.model._meta.local_managers): - return qs - ordering = qs.model._meta.ordering - return qs.order_by(*ordering) + return reapply_model_ordering(qs) def get_serializer(self, *args, **kwargs): # If a list of objects has been provided, initialize the serializer with many=True diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index fa9683c2c..48371ebee 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -27,8 +27,8 @@ from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViol from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fields from utilities.forms.bulk_import import BulkImportForm from utilities.htmx import htmx_partial -from utilities.mptt import TreeManager from utilities.permissions import get_permission_for_model +from utilities.query import reapply_model_ordering from utilities.views import GetReturnURLMixin, get_viewname from .base import BaseMultiObjectView from .mixins import ActionsMixin, TableMixin @@ -127,16 +127,8 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): # def get_queryset(self, request): - """ - Reapply model-level ordering in case it has been lost through .annotate(). - https://code.djangoproject.com/ticket/32811 - """ qs = super().get_queryset(request) - # MPTT-based models are exempt from this; use caution when annotating querysets of these models - if any(isinstance(manager, TreeManager) for manager in qs.model._meta.local_managers): - return qs - ordering = qs.model._meta.ordering - return qs.order_by(*ordering) + return reapply_model_ordering(qs) def get(self, request): """ diff --git a/netbox/utilities/query.py b/netbox/utilities/query.py index 3a355ab67..b254df582 100644 --- a/netbox/utilities/query.py +++ b/netbox/utilities/query.py @@ -1,9 +1,12 @@ -from django.db.models import Count, OuterRef, Subquery +from django.db.models import Count, OuterRef, Subquery, QuerySet from django.db.models.functions import Coalesce +from utilities.mptt import TreeManager + __all__ = ( 'count_related', 'dict_to_filter_params', + 'reapply_model_ordering', ) @@ -54,3 +57,15 @@ def dict_to_filter_params(d, prefix=''): else: params[k] = val return params + + +def reapply_model_ordering(queryset: QuerySet) -> QuerySet: + """ + Reapply model-level ordering in case it has been lost through .annotate(). + https://code.djangoproject.com/ticket/32811 + """ + # MPTT-based models are exempt from this; use caution when annotating querysets of these models + if any(isinstance(manager, TreeManager) for manager in queryset.model._meta.local_managers): + return queryset + ordering = queryset.model._meta.ordering + return queryset.order_by(*ordering)