Repository: netbox-community/netbox
Pull request: Fixes #18879 - Add prefix filtering by assigned VLAN Group
<!--
    Thank you for your interest in contributing to NetBox! Please note that
    our contribution policy requires that a feature request or bug report be
    approved and assigned prior to opening a pull request. This helps avoid
    waste time and effort on a proposed change that we might not be able to
    accept.

    IF YOUR PULL REQUEST DOES NOT REFERENCE AN ISSUE WHICH HAS BEEN ASSIGNED
    TO YOU, IT WILL BE CLOSED AUTOMATICALLY.

    Please specify your assigned issue number on the line below.
-->
### Fixes #18879 - Add prefix filtering by assigned VLAN Group

#### Overview

This PR enhances NetBox by enabling filtering of prefixes by the assigned VLAN Group. Users can now filter using both quick search (by VLAN Group slug) and FilterForm (by ID).

#### Changes Introduced

- **IPAM FilterSet:** Added VLAN Group filters to support filtering by both ID and slug.
- **IPAM FilterForm:** Introduced a `vlan_group_id` filter for direct filtering by VLAN Group.
- **Testing:** Added tests to validate VLAN Group filtering.

Thank you for reviewing this PR!

Problem statement:
Add prefix filtering by assigned VLAN group
### NetBox version

v4.2.4

### Feature type

New functionality

### Proposed functionality

Please make it possible to use both `Quick search` and `Filter` to be able to properly filter out prefixes by specific `VLAN Groups` they are assigned to.

Currently it's possible to only sort the table (asc/desc) by that property, but filtering isn't avaible.

### Use case

Having many networks, prefixes and vlans across multiple sites, it would be very helpful to be able to use a proper `filter` and `quick search` to be able to  filter for prefixes assigned to specific `vlan groups`.

Right now the only way to do so is to have the prefixes page show 1000 entries and use the browser's search tool to highlight specific vlan groups.

### Database changes

_No response_

### External dependencies

_No response_

Hints from issue discussion:

Commit message: feat(ipam): Add VLAN group filter in IPAM FilterForm

Introduces a `vlan_group_id` filter to IPAM forms for filtering based on
VLAN groups.

Patch to review:
diff --git a/netbox/ipam/filtersets.py b/netbox/ipam/filtersets.py
index 5c8dbc78045..e16885ba003 100644
--- a/netbox/ipam/filtersets.py
+++ b/netbox/ipam/filtersets.py
@@ -351,6 +351,18 @@ class PrefixFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet, C
         to_field_name='rd',
         label=_('VRF (RD)'),
     )
+    vlan_group_id = django_filters.ModelMultipleChoiceFilter(
+        field_name='vlan__group',
+        queryset=VLANGroup.objects.all(),
+        to_field_name="id",
+        label=_('VLAN Group (ID)'),
+    )
+    vlan_group = django_filters.ModelMultipleChoiceFilter(
+        field_name='vlan__group__slug',
+        queryset=VLANGroup.objects.all(),
+        to_field_name="slug",
+        label=_('VLAN Group (slug)'),
+    )
     vlan_id = django_filters.ModelMultipleChoiceFilter(
         queryset=VLAN.objects.all(),
         label=_('VLAN (ID)'),
diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py
index e51ae6daeef..4cfe9f872ac 100644
--- a/netbox/ipam/forms/filtersets.py
+++ b/netbox/ipam/forms/filtersets.py
@@ -176,7 +176,7 @@ class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFil
             'within_include', 'family', 'status', 'role_id', 'mask_length', 'is_pool', 'mark_utilized',
             name=_('Addressing')
         ),
-        FieldSet('vlan_id', name=_('VLAN Assignment')),
+        FieldSet('vlan_group_id', 'vlan_id', name=_('VLAN Assignment')),
         FieldSet('vrf_id', 'present_in_vrf_id', name=_('VRF')),
         FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')),
         FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
@@ -260,6 +260,11 @@ class PrefixFilterForm(ContactModelFilterForm, TenancyFilterForm, NetBoxModelFil
             choices=BOOLEAN_WITH_BLANK_CHOICES
         )
     )
+    vlan_group_id = DynamicModelMultipleChoiceField(
+        queryset=VLANGroup.objects.all(),
+        required=False,
+        label=_('VLAN Group'),
+    )
     vlan_id = DynamicModelMultipleChoiceField(
         queryset=VLAN.objects.all(),
         required=False,


Code review comments:
diff_hunk: @@ -850,6 +857,15 @@ def test_site(self):
         params = {'site': [sites[0].slug, sites[1].slug]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
 
+    def test_vlan_group(self):
+        vlan_groups = VLANGroup.objects.all()[:2]
+        params = {'vlan_group_id': [vlan_groups[0].pk, vlan_groups[1].pk]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
+        params = {'vlan_group': [vlan_groups[0].slug]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
path: netbox/ipam/tests/test_filtersets.py
text: @user1:
These two lines can be removed; the following two lines provide sufficient test coverage.
```suggestion
```