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']
             ['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):
     def test_create_site(self):
 
 
         data = {
         data = {
@@ -1753,7 +1763,8 @@ class DeviceTest(APITestCase):
 
 
         super().setUp()
         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')
         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')
         manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
         self.devicetype1 = DeviceType.objects.create(
         self.devicetype1 = DeviceType.objects.create(
@@ -1828,6 +1839,21 @@ class DeviceTest(APITestCase):
             ['display_name', 'id', 'name', 'url']
             ['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):
     def test_create_device(self):
 
 
         data = {
         data = {

+ 7 - 2
netbox/utilities/filters.py

@@ -1,9 +1,8 @@
 import django_filters
 import django_filters
+from dcim.forms import MACAddressField
 from django import forms
 from django import forms
 from django.conf import settings
 from django.conf import settings
 from django.db import models
 from django.db import models
-
-from dcim.forms import MACAddressField
 from extras.models import Tag
 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>]
     Filters for a set of Models, including all descendant models within a Tree.  Example: [<Region: R1>,<Region: R2>]
     """
     """
+
     def filter(self, qs, value):
     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]
         value = [node.get_descendants(include_self=True) for node in value]
         return super().filter(qs, 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 rest_framework import status
 
 
 from dcim.constants import IFACE_TYPE_VIRTUAL, IFACE_MODE_TAGGED
 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 ipam.models import IPAddress, VLAN
 from utilities.testing import APITestCase
 from utilities.testing import APITestCase
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@@ -330,9 +330,14 @@ class VirtualMachineTest(APITestCase):
 
 
         super().setUp()
         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_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')
         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.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)
         self.virtualmachine2 = VirtualMachine.objects.create(name='Test Virtual Machine 2', cluster=self.cluster1)
@@ -370,6 +375,15 @@ class VirtualMachineTest(APITestCase):
             ['id', 'name', 'url']
             ['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):
     def test_create_virtualmachine(self):
 
 
         data = {
         data = {
@@ -430,14 +444,9 @@ class VirtualMachineTest(APITestCase):
         ip4_address = IPAddress.objects.create(address=IPNetwork('192.0.2.1/24'), interface=interface)
         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)
         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 = {
         data = {
             'name': 'Test Virtual Machine X',
             'name': 'Test Virtual Machine X',
-            'cluster': cluster2.pk,
+            'cluster': self.cluster2.pk,
             'primary_ip4': ip4_address.pk,
             'primary_ip4': ip4_address.pk,
             'primary_ip6': ip6_address.pk,
             'primary_ip6': ip6_address.pk,
         }
         }