From 4f45328c77d8e892ce832517142639c8bb4346f5 Mon Sep 17 00:00:00 2001 From: bctiemann Date: Thu, 13 Mar 2025 16:38:33 -0400 Subject: [PATCH] Fixes: #18863 - Exempt MPTT-based models from centrally applying ordering on querysets (#18867) --- netbox/netbox/api/viewsets/__init__.py | 8 ++------ netbox/netbox/views/generic/bulk_views.py | 8 ++------ netbox/utilities/query.py | 17 ++++++++++++++++- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index 76fc7e0b1..649510239 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -12,6 +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.query import reapply_model_ordering from . import mixins __all__ = ( @@ -122,13 +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() - 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 967c0eadb..48371ebee 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -28,6 +28,7 @@ from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fiel from utilities.forms.bulk_import import BulkImportForm from utilities.htmx import htmx_partial 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 @@ -126,13 +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) - 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)