Forráskód Böngészése

Closes #8995: Enable arbitrary ordering of REST API results

jeremystretch 3 éve
szülő
commit
03535ce50b

+ 7 - 0
docs/release-notes/version-3.3.md

@@ -0,0 +1,7 @@
+# NetBox v3.3
+
+## v3.3.0 (FUTURE)
+
+### Enhancements
+
+* [#8995](https://github.com/netbox-community/netbox/issues/8995) - Enable arbitrary ordering of REST API results

+ 20 - 0
docs/rest-api/filtering.md

@@ -106,3 +106,23 @@ expression: `n`. Here is an example of a lookup expression on a foreign key, it
 ```no-highlight
 GET /api/ipam/vlans/?group_id__n=3203
 ```
+
+## Ordering Objects
+
+To order results by a particular field, include the `ordering` query parameter. For example, order the list of sites according to their facility values:
+
+```no-highlight
+GET /api/dcim/sites/?ordering=facility
+```
+
+To invert the ordering, prepend a hyphen to the field name:
+
+```no-highlight
+GET /api/dcim/sites/?ordering=-facility
+```
+
+Multiple fields can be specified by separating the field names with a comma. For example:
+
+```no-highlight
+GET /api/dcim/sites/?ordering=facility,-name
+```

+ 2 - 1
netbox/netbox/settings.py

@@ -26,7 +26,7 @@ django.utils.encoding.force_text = force_str
 # Environment setup
 #
 
-VERSION = '3.2.1-dev'
+VERSION = '3.3.0-dev'
 
 # Hostname
 HOSTNAME = platform.node()
@@ -469,6 +469,7 @@ REST_FRAMEWORK = {
     ),
     'DEFAULT_FILTER_BACKENDS': (
         'django_filters.rest_framework.DjangoFilterBackend',
+        'rest_framework.filters.OrderingFilter',
     ),
     'DEFAULT_METADATA_CLASS': 'netbox.api.metadata.BulkOperationMetadata',
     'DEFAULT_PAGINATION_CLASS': 'netbox.api.pagination.OptionalLimitOffsetPagination',

+ 58 - 0
netbox/utilities/tests/test_api.py

@@ -176,6 +176,64 @@ class APIPaginationTestCase(APITestCase):
         self.assertEqual(len(response.data['results']), 100)
 
 
+class APIOrderingTestCase(APITestCase):
+    user_permissions = ('dcim.view_site',)
+
+    @classmethod
+    def setUpTestData(cls):
+        cls.url = reverse('dcim-api:site-list')
+
+        sites = (
+            Site(name='Site 1', slug='site-1', facility='C', description='Z'),
+            Site(name='Site 2', slug='site-2', facility='C', description='Y'),
+            Site(name='Site 3', slug='site-3', facility='B', description='X'),
+            Site(name='Site 4', slug='site-4', facility='B', description='W'),
+            Site(name='Site 5', slug='site-5', facility='A', description='V'),
+            Site(name='Site 6', slug='site-6', facility='A', description='U'),
+        )
+        Site.objects.bulk_create(sites)
+
+    def test_default_order(self):
+        response = self.client.get(self.url, format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        self.assertEqual(response.data['count'], 6)
+        self.assertListEqual(
+            [s['name'] for s in response.data['results']],
+            ['Site 1', 'Site 2', 'Site 3', 'Site 4', 'Site 5', 'Site 6']
+        )
+
+    def test_order_single_field(self):
+        response = self.client.get(f'{self.url}?ordering=description', format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        self.assertEqual(response.data['count'], 6)
+        self.assertListEqual(
+            [s['name'] for s in response.data['results']],
+            ['Site 6', 'Site 5', 'Site 4', 'Site 3', 'Site 2', 'Site 1']
+        )
+
+    def test_order_reversed(self):
+        response = self.client.get(f'{self.url}?ordering=-name', format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        self.assertEqual(response.data['count'], 6)
+        self.assertListEqual(
+            [s['name'] for s in response.data['results']],
+            ['Site 6', 'Site 5', 'Site 4', 'Site 3', 'Site 2', 'Site 1']
+        )
+
+    def test_order_multiple_fields(self):
+        response = self.client.get(f'{self.url}?ordering=facility,name', format='json', **self.header)
+
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        self.assertEqual(response.data['count'], 6)
+        self.assertListEqual(
+            [s['name'] for s in response.data['results']],
+            ['Site 5', 'Site 6', 'Site 3', 'Site 4', 'Site 1', 'Site 2']
+        )
+
+
 class APIDocsTestCase(TestCase):
 
     def setUp(self):