kobayashi 6 лет назад
Родитель
Сommit
766b5dff24
3 измененных файлов с 51 добавлено и 11 удалено
  1. 27 1
      netbox/dcim/tests/test_api.py
  2. 7 2
      netbox/utilities/filters.py
  3. 17 8
      netbox/virtualization/tests/test_api.py

+ 27 - 1
netbox/dcim/tests/test_api.py

@@ -174,6 +174,16 @@ class SiteTest(APITestCase):
             ['id', 'name', 'slug', 'url']
         )
 
+    def test_list_sites_null_region(self):
+
+        Site.objects.create(name='Test Site Null Region1', slug='test-site-no-region1')
+        Site.objects.create(name='Test Site Null Region2', slug='test-site-no-region2')
+
+        url = reverse('dcim-api:site-list')
+        response = self.client.get('{}?region=null'.format(url), **self.header)
+
+        self.assertEqual(response.data['count'], 2)
+
     def test_create_site(self):
 
         data = {
@@ -1753,7 +1763,8 @@ class DeviceTest(APITestCase):
 
         super().setUp()
 
-        self.site1 = Site.objects.create(name='Test Site 1', slug='test-site-1')
+        region = Region.objects.create(name='Test Region', slug='test-region')
+        self.site1 = Site.objects.create(region=region, name='Test Site 1', slug='test-site-1')
         self.site2 = Site.objects.create(name='Test Site 2', slug='test-site-2')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         self.devicetype1 = DeviceType.objects.create(
@@ -1828,6 +1839,21 @@ class DeviceTest(APITestCase):
             ['display_name', 'id', 'name', 'url']
         )
 
+    def test_list_device_null_region(self):
+
+        Device.objects.create(
+            device_type=self.devicetype1,
+            device_role=self.devicerole1,
+            name='Test Device Null Region',
+            site=self.site2,
+            cluster=self.cluster1
+        )
+
+        url = reverse('dcim-api:device-list')
+        response = self.client.get('{}?region=null'.format(url), **self.header)
+
+        self.assertEqual(response.data['count'], 1)
+
     def test_create_device(self):
 
         data = {

+ 7 - 2
netbox/utilities/filters.py

@@ -1,9 +1,8 @@
 import django_filters
+from dcim.forms import MACAddressField
 from django import forms
 from django.conf import settings
 from django.db import models
-
-from dcim.forms import MACAddressField
 from extras.models import Tag
 
 
@@ -62,7 +61,13 @@ class TreeNodeMultipleChoiceFilter(django_filters.ModelMultipleChoiceFilter):
     """
     Filters for a set of Models, including all descendant models within a Tree.  Example: [<Region: R1>,<Region: R2>]
     """
+
     def filter(self, qs, value):
+        if settings.FILTERS_NULL_CHOICE_VALUE in value:
+            # Filtering by null value. Example: region=null
+            qs = self.get_method(qs)(**{self.field_name.replace('in', 'isnull'): True})
+            return qs.distinct() if self.distinct else qs
+
         value = [node.get_descendants(include_self=True) for node in value]
         return super().filter(qs, value)
 

+ 17 - 8
netbox/virtualization/tests/test_api.py

@@ -3,7 +3,7 @@ from netaddr import IPNetwork
 from rest_framework import status
 
 from dcim.constants import IFACE_TYPE_VIRTUAL, IFACE_MODE_TAGGED
-from dcim.models import Interface
+from dcim.models import Interface, Region, Site
 from ipam.models import IPAddress, VLAN
 from utilities.testing import APITestCase
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@@ -330,9 +330,14 @@ class VirtualMachineTest(APITestCase):
 
         super().setUp()
 
+        region = Region.objects.create(name='Test Region 1', slug='test-region-1')
+        site1 = Site.objects.create(region=region, name='Test Site 1', slug='test-site-1')
+        site2 = Site.objects.create(name='Test Site 2', slug='test-site-2')
+
         cluster_type = ClusterType.objects.create(name='Test Cluster Type 1', slug='test-cluster-type-1')
         cluster_group = ClusterGroup.objects.create(name='Test Cluster Group 1', slug='test-cluster-group-1')
-        self.cluster1 = Cluster.objects.create(name='Test Cluster 1', type=cluster_type, group=cluster_group)
+        self.cluster1 = Cluster.objects.create(name='Test Cluster 1', type=cluster_type, group=cluster_group, site=site1)
+        self.cluster2 = Cluster.objects.create(name='Test Cluster 2', type=cluster_type, group=cluster_group, site=site2)
 
         self.virtualmachine1 = VirtualMachine.objects.create(name='Test Virtual Machine 1', cluster=self.cluster1)
         self.virtualmachine2 = VirtualMachine.objects.create(name='Test Virtual Machine 2', cluster=self.cluster1)
@@ -370,6 +375,15 @@ class VirtualMachineTest(APITestCase):
             ['id', 'name', 'url']
         )
 
+    def test_list_virtualmachines_null_region(self):
+
+        VirtualMachine.objects.create(name='Test Virtual Machine Null Region', cluster=self.cluster2)
+
+        url = reverse('virtualization-api:virtualmachine-list')
+        response = self.client.get('{}?region=null'.format(url), **self.header)
+
+        self.assertEqual(response.data['count'], 1)
+
     def test_create_virtualmachine(self):
 
         data = {
@@ -430,14 +444,9 @@ class VirtualMachineTest(APITestCase):
         ip4_address = IPAddress.objects.create(address=IPNetwork('192.0.2.1/24'), interface=interface)
         ip6_address = IPAddress.objects.create(address=IPNetwork('2001:db8::1/64'), interface=interface)
 
-        cluster2 = Cluster.objects.create(
-            name='Test Cluster 2',
-            type=ClusterType.objects.first(),
-            group=ClusterGroup.objects.first()
-        )
         data = {
             'name': 'Test Virtual Machine X',
-            'cluster': cluster2.pk,
+            'cluster': self.cluster2.pk,
             'primary_ip4': ip4_address.pk,
             'primary_ip6': ip6_address.pk,
         }