Revert OrderingFilter
This commit is contained in:
parent
17b2017e6b
commit
a633e80804
|
|
@ -1,7 +1,6 @@
|
|||
from django.http import Http404, HttpResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django_rq.queues import get_redis_connection
|
||||
from django_rq.settings import get_queues_list
|
||||
from django_rq.utils import get_statistics
|
||||
|
|
@ -20,7 +19,6 @@ from core.jobs import SyncDataSourceJob
|
|||
from core.models import *
|
||||
from core.utils import delete_rq_job, enqueue_rq_job, get_rq_jobs, requeue_rq_job, stop_rq_job
|
||||
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
|
||||
from netbox.api.filter_backends import OrderingFilter
|
||||
from netbox.api.metadata import ContentTypeMetadata
|
||||
from netbox.api.pagination import LimitOffsetListPagination
|
||||
from netbox.api.viewsets import NetBoxModelViewSet, NetBoxReadOnlyModelViewSet
|
||||
|
|
@ -73,11 +71,6 @@ class JobViewSet(NetBoxReadOnlyModelViewSet):
|
|||
queryset = Job.objects.all()
|
||||
serializer_class = serializers.JobSerializer
|
||||
filterset_class = filtersets.JobFilterSet
|
||||
filter_backends = (DjangoFilterBackend, OrderingFilter)
|
||||
# Order by elapsed time for jobs which are still running, matching the jobs table in the UI
|
||||
ordering_expressions = {
|
||||
'execution_time': Job.elapsed_time_expression(),
|
||||
}
|
||||
|
||||
|
||||
class ObjectChangeViewSet(NetBoxReadOnlyModelViewSet):
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ class JobTestCase(
|
|||
)
|
||||
|
||||
def test_list_objects_by_execution_time(self):
|
||||
"""The Job list endpoint supports filtering by execution_time."""
|
||||
"""The Job list endpoint supports filtering and ordering by execution_time."""
|
||||
self.add_permissions('core.view_job')
|
||||
url = reverse('core-api:job-list')
|
||||
|
||||
|
|
@ -221,30 +221,10 @@ class JobTestCase(
|
|||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['count'], 1)
|
||||
|
||||
def test_ordering_by_execution_time(self):
|
||||
"""
|
||||
Ordering by execution_time must place jobs with no execution time last in both directions,
|
||||
and must rank a running job by its elapsed time (matching the jobs table in the UI).
|
||||
"""
|
||||
self.add_permissions('core.view_job')
|
||||
url = reverse('core-api:job-list')
|
||||
|
||||
# 'Job 2' is running; give it a start time so it has an elapsed time exceeding Job 3's 90s
|
||||
Job.objects.filter(name='Job 2').update(started=timezone.now() - timezone.timedelta(hours=1))
|
||||
|
||||
response = self.client.get(f'{url}?ordering=-execution_time', **self.header)
|
||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
[job['name'] for job in response.data['results']],
|
||||
['Job 2', 'Job 3', 'Job 1'],
|
||||
)
|
||||
|
||||
# Ordering by execution_time should be accepted (NULLs sort to one end)
|
||||
response = self.client.get(f'{url}?ordering=execution_time', **self.header)
|
||||
self.assertHttpStatus(response, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
[job['name'] for job in response.data['results']],
|
||||
['Job 3', 'Job 2', 'Job 1'],
|
||||
)
|
||||
self.assertEqual(response.data['count'], 3)
|
||||
|
||||
|
||||
class BackgroundTaskTestCase(RQQueueTestMixin, TestCase):
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
from django.db.models import F
|
||||
from rest_framework import filters
|
||||
|
||||
__all__ = (
|
||||
'OrderingFilter',
|
||||
)
|
||||
|
||||
|
||||
class OrderingFilter(filters.OrderingFilter):
|
||||
"""
|
||||
Extends DRF's OrderingFilter to sort null values last irrespective of the sort direction, and to
|
||||
append a stable tiebreaker so that paginating through tied rows cannot skip or repeat them.
|
||||
(PostgreSQL sorts nulls first when ordering descending, which pushes rows with no value to the
|
||||
top of a descending sort.)
|
||||
|
||||
A viewset may map a field name to a query expression via `ordering_expressions` to order by
|
||||
something other than the named column; this is used where the value presented to the user is
|
||||
computed rather than stored.
|
||||
"""
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
if not (ordering := self.get_ordering(request, queryset, view)):
|
||||
return queryset
|
||||
|
||||
expressions = getattr(view, 'ordering_expressions', {})
|
||||
terms = []
|
||||
for term in ordering:
|
||||
if descending := term.startswith('-'):
|
||||
term = term[1:]
|
||||
expression = expressions[term] if term in expressions else F(term)
|
||||
terms.append(
|
||||
expression.desc(nulls_last=True) if descending else expression.asc(nulls_last=True)
|
||||
)
|
||||
|
||||
return queryset.order_by(*terms, 'pk')
|
||||
Loading…
Reference in New Issue