Przeglądaj źródła

fix(ipam): Support multiple values for VLANGroup scope filters

Replace NumberFilter with MultiValueNumberFilter for VLANGroup scope
filters (Region, Site Group, Site, Location, Rack Group, Rack,
Cluster Group, Cluster). Update filter_scope method to use `__in`
lookup and add comprehensive test coverage for multi-value filtering.

Fixes #22671
Martin Hauser 1 dzień temu
rodzic
commit
6437626d6d
2 zmienionych plików z 101 dodań i 17 usunięć
  1. 9 9
      netbox/ipam/filtersets.py
  2. 92 8
      netbox/ipam/tests/test_filtersets.py

+ 9 - 9
netbox/ipam/filtersets.py

@@ -946,28 +946,28 @@ class FHRPGroupAssignmentFilterSet(ChangeLoggedModelFilterSet):
 @register_filterset
 class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
     scope_type = MultiValueContentTypeFilter()
-    region = django_filters.NumberFilter(
+    region = MultiValueNumberFilter(
         method='filter_scope'
     )
-    site_group = django_filters.NumberFilter(
+    site_group = MultiValueNumberFilter(
         method='filter_scope'
     )
-    site = django_filters.NumberFilter(
+    site = MultiValueNumberFilter(
         method='filter_scope'
     )
-    location = django_filters.NumberFilter(
+    location = MultiValueNumberFilter(
         method='filter_scope'
     )
-    rack_group = django_filters.NumberFilter(
+    rack_group = MultiValueNumberFilter(
         method='filter_scope'
     )
-    rack = django_filters.NumberFilter(
+    rack = MultiValueNumberFilter(
         method='filter_scope'
     )
-    cluster_group = django_filters.NumberFilter(
+    cluster_group = MultiValueNumberFilter(
         method='filter_scope'
     )
-    cluster = django_filters.NumberFilter(
+    cluster = MultiValueNumberFilter(
         method='filter_scope'
     )
     contains_vid = django_filters.NumberFilter(
@@ -992,7 +992,7 @@ class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
         model_name = name.replace('_', '')
         return queryset.filter(
             scope_type=ContentType.objects.get(model=model_name),
-            scope_id=value
+            scope_id__in=value
         )
 
 

+ 92 - 8
netbox/ipam/tests/test_filtersets.py

@@ -5,7 +5,19 @@ from netaddr import IPNetwork
 
 from circuits.models import Provider
 from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices
-from dcim.models import Device, DeviceRole, DeviceType, Interface, Location, Manufacturer, Rack, Region, Site, SiteGroup
+from dcim.models import (
+    Device,
+    DeviceRole,
+    DeviceType,
+    Interface,
+    Location,
+    Manufacturer,
+    Rack,
+    RackGroup,
+    Region,
+    Site,
+    SiteGroup,
+)
 from ipam.choices import *
 from ipam.filtersets import *
 from ipam.models import *
@@ -1779,32 +1791,104 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
 
     def test_region(self):
-        params = {'region': Region.objects.first().pk}
+        regions = (
+            Region.objects.get(slug='region-1'),
+            Region.objects.create(name='Region 2', slug='region-2'),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=regions[1])
+
+        params = {'region': [regions[0].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'region': [regions[0].pk, regions[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_site_group(self):
-        params = {'site_group': SiteGroup.objects.first().pk}
+        site_groups = (
+            SiteGroup.objects.get(slug='site-group-1'),
+            SiteGroup.objects.create(name='Site Group 2', slug='site-group-2'),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=site_groups[1])
+
+        params = {'site_group': [site_groups[0].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'site_group': [site_groups[0].pk, site_groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_site(self):
-        params = {'site': Site.objects.first().pk}
+        sites = (
+            Site.objects.get(slug='site-1'),
+            Site.objects.create(name='Site 2', slug='site-2'),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=sites[1])
+
+        params = {'site': [sites[0].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'site': [sites[0].pk, sites[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_location(self):
-        params = {'location': Location.objects.first().pk}
+        site = Site.objects.get(slug='site-1')
+        locations = (
+            Location.objects.get(slug='location-1'),
+            Location.objects.create(name='Location 2', slug='location-2', site=site),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=locations[1])
+
+        params = {'location': [locations[0].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'location': [locations[0].pk, locations[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+    def test_rack_group(self):
+        rack_groups = (
+            RackGroup.objects.create(name='Rack Group 1', slug='rack-group-1'),
+            RackGroup.objects.create(name='Rack Group 2', slug='rack-group-2'),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=rack_groups[0])
+        VLANGroup.objects.create(name='VLAN Group 10', slug='vlan-group-10', scope=rack_groups[1])
+
+        params = {'rack_group': [rack_groups[0].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'rack_group': [rack_groups[0].pk, rack_groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_rack(self):
-        params = {'rack': Rack.objects.first().pk}
+        site = Site.objects.get(slug='site-1')
+        racks = (
+            Rack.objects.get(name='Rack 1'),
+            Rack.objects.create(name='Rack 2', site=site),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=racks[1])
+
+        params = {'rack': [racks[0].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'rack': [racks[0].pk, racks[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_cluster_group(self):
-        params = {'cluster_group': ClusterGroup.objects.first().pk}
+        cluster_groups = (
+            ClusterGroup.objects.get(slug='cluster-group-1'),
+            ClusterGroup.objects.create(name='Cluster Group 2', slug='cluster-group-2'),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=cluster_groups[1])
+
+        params = {'cluster_group': [cluster_groups[0].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'cluster_group': [cluster_groups[0].pk, cluster_groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_cluster(self):
-        params = {'cluster': Cluster.objects.first().pk}
+        cluster_type = ClusterType.objects.get(slug='cluster-type-1')
+        clusters = (
+            Cluster.objects.get(name='Cluster 1'),
+            Cluster.objects.create(name='Cluster 2', type=cluster_type),
+        )
+        VLANGroup.objects.create(name='VLAN Group 9', slug='vlan-group-9', scope=clusters[1])
+
+        params = {'cluster': [clusters[0].pk]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+        params = {'cluster': [clusters[0].pk, clusters[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
     def test_tenant(self):
         tenants = Tenant.objects.all()[:2]