diff --git a/netbox/ipam/filters.py b/netbox/ipam/filters.py index 1f8fd2caf..bf14b80d6 100644 --- a/netbox/ipam/filters.py +++ b/netbox/ipam/filters.py @@ -4,10 +4,10 @@ from django.core.exceptions import ValidationError from django.db.models import Q from netaddr.core import AddrFormatError -from dcim.models import Site, Device, Interface +from dcim.models import Device, Interface, Region, Site from extras.filters import CustomFieldFilterSet, CreatedUpdatedFilterSet from tenancy.filtersets import TenancyFilterSet -from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter +from utilities.filters import NameSlugSearchFilterSet, NumericInFilter, TagFilter, TreeNodeMultipleChoiceFilter from virtualization.models import VirtualMachine from .constants import * from .models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF @@ -149,6 +149,17 @@ class PrefixFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterS to_field_name='rd', label='VRF (RD)', ) + region_id = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='site__region__in', + label='Region (ID)', + ) + region = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='site__region__in', + to_field_name='slug', + label='Region (slug)', + ) site_id = django_filters.ModelMultipleChoiceFilter( queryset=Site.objects.all(), label='Site (ID)', @@ -375,6 +386,17 @@ class IPAddressFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilt class VLANGroupFilter(NameSlugSearchFilterSet): + region_id = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='site__region__in', + label='Region (ID)', + ) + region = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='site__region__in', + to_field_name='slug', + label='Region (slug)', + ) site_id = django_filters.ModelMultipleChoiceFilter( queryset=Site.objects.all(), label='Site (ID)', @@ -400,6 +422,17 @@ class VLANFilter(TenancyFilterSet, CustomFieldFilterSet, CreatedUpdatedFilterSet method='search', label='Search', ) + region_id = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='site__region__in', + label='Region (ID)', + ) + region = TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='site__region__in', + to_field_name='slug', + label='Region (slug)', + ) site_id = django_filters.ModelMultipleChoiceFilter( queryset=Site.objects.all(), label='Site (ID)', diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 44056653b..413e72eaf 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -3,7 +3,7 @@ from django.core.exceptions import MultipleObjectsReturned from django.core.validators import MaxValueValidator, MinValueValidator from taggit.forms import TagField -from dcim.models import Site, Rack, Device, Interface +from dcim.models import Device, Interface, Rack, Region, Site from extras.forms import AddRemoveTagsForm, CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm from tenancy.forms import TenancyFilterForm, TenancyForm from tenancy.models import Tenant @@ -492,8 +492,8 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): model = Prefix field_order = [ - 'q', 'within_include', 'family', 'mask_length', 'vrf_id', 'status', 'site', 'role', 'tenant_group', 'tenant', - 'is_pool', 'expand', + 'q', 'within_include', 'family', 'mask_length', 'vrf_id', 'status', 'region', 'site', 'role', 'tenant_group', + 'tenant', 'is_pool', 'expand', ] q = forms.CharField( required=False, @@ -534,6 +534,18 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm) required=False, widget=StaticSelect2Multiple() ) + region = FilterChoiceField( + queryset=Region.objects.all(), + to_field_name='slug', + required=False, + widget=APISelectMultiple( + api_url="/api/dcim/regions/", + value_field="slug", + filter_for={ + 'site': 'region' + } + ) + ) site = FilterChoiceField( queryset=Site.objects.all(), to_field_name='slug', @@ -1034,6 +1046,18 @@ class VLANGroupCSVForm(forms.ModelForm): class VLANGroupFilterForm(BootstrapMixin, forms.Form): + region = FilterChoiceField( + queryset=Region.objects.all(), + to_field_name='slug', + required=False, + widget=APISelectMultiple( + api_url="/api/dcim/regions/", + value_field="slug", + filter_for={ + 'site': 'region', + } + ) + ) site = FilterChoiceField( queryset=Site.objects.all(), to_field_name='slug', @@ -1215,11 +1239,24 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): model = VLAN - field_order = ['q', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant'] + field_order = ['q', 'region', 'site', 'group_id', 'status', 'role', 'tenant_group', 'tenant'] q = forms.CharField( required=False, label='Search' ) + region = FilterChoiceField( + queryset=Region.objects.all(), + to_field_name='slug', + required=False, + widget=APISelectMultiple( + api_url="/api/dcim/regions/", + value_field="slug", + filter_for={ + 'site': 'region', + 'group_id': 'region' + } + ) + ) site = FilterChoiceField( queryset=Site.objects.all(), to_field_name='slug',