diff --git a/netbox/netbox/filtersets.py b/netbox/netbox/filtersets.py index 637a40bf1..934326fb6 100644 --- a/netbox/netbox/filtersets.py +++ b/netbox/netbox/filtersets.py @@ -9,6 +9,7 @@ from django.utils.translation import gettext as _ from core.choices import ObjectChangeActionChoices from core.models import ObjectChange +from dcim.models import Location, Region, Site, SiteGroup from extras.choices import CustomFieldFilterLogicChoices from extras.filters import TagFilter from extras.models import CustomField, SavedFilter @@ -325,3 +326,60 @@ class OrganizationalModelFilterSet(NetBoxModelFilterSet): models.Q(slug__icontains=value) | models.Q(description__icontains=value) ) + + +class ScopeModelFilterSet(BaseFilterSet): + """ + Provides additional filtering functionality for location, site, etc.. for Scoped models. + """ + scope_type = filters.ContentTypeFilter() + region_id = filters.TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='_region', + lookup_expr='in', + label=_('Region (ID)'), + ) + region = filters.TreeNodeMultipleChoiceFilter( + queryset=Region.objects.all(), + field_name='_region', + lookup_expr='in', + to_field_name='slug', + label=_('Region (slug)'), + ) + site_group_id = filters.TreeNodeMultipleChoiceFilter( + queryset=SiteGroup.objects.all(), + field_name='_sitegroup', + lookup_expr='in', + label=_('Site group (ID)'), + ) + site_group = filters.TreeNodeMultipleChoiceFilter( + queryset=SiteGroup.objects.all(), + field_name='_sitegroup', + lookup_expr='in', + to_field_name='slug', + label=_('Site group (slug)'), + ) + site_id = django_filters.ModelMultipleChoiceFilter( + queryset=Site.objects.all(), + field_name='_site', + label=_('Site (ID)'), + ) + site = django_filters.ModelMultipleChoiceFilter( + field_name='_site__slug', + queryset=Site.objects.all(), + to_field_name='slug', + label=_('Site (slug)'), + ) + location_id = filters.TreeNodeMultipleChoiceFilter( + queryset=Location.objects.all(), + field_name='_location', + lookup_expr='in', + label=_('Location (ID)'), + ) + location = filters.TreeNodeMultipleChoiceFilter( + queryset=Location.objects.all(), + field_name='_location', + lookup_expr='in', + to_field_name='slug', + label=_('Location (slug)'), + ) diff --git a/netbox/netbox/forms/base.py b/netbox/netbox/forms/base.py index 59fc3abc5..722c95fc3 100644 --- a/netbox/netbox/forms/base.py +++ b/netbox/netbox/forms/base.py @@ -193,7 +193,24 @@ class NetBoxModelFilterSetForm(CustomFieldsMixin, SavedFiltersMixin, forms.Form) class ScopeForm(forms.Form): - def _set_scoped_values(): + def __init__(self, *args, **kwargs): + instance = kwargs.get('instance') + initial = kwargs.get('initial', {}) + + if instance is not None and instance.scope: + initial['scope'] = instance.scope + kwargs['initial'] = initial + + super().__init__(*args, **kwargs) + self._set_scoped_values() + + def clean(self): + super().clean() + + # Assign the selected scope (if any) + self.instance.scope = self.cleaned_data.get('scope') + + def _set_scoped_values(self): if scope_type_id := get_field_value(self, 'scope_type'): try: scope_type = ContentType.objects.get(pk=scope_type_id) diff --git a/netbox/virtualization/forms/model_forms.py b/netbox/virtualization/forms/model_forms.py index 5337bd5f5..b05dd27da 100644 --- a/netbox/virtualization/forms/model_forms.py +++ b/netbox/virtualization/forms/model_forms.py @@ -96,23 +96,6 @@ class ClusterForm(TenancyForm, ScopeForm, NetBoxModelForm): 'name', 'type', 'group', 'status', 'tenant', 'scope_type', 'description', 'comments', 'tags', ) - def __init__(self, *args, **kwargs): - instance = kwargs.get('instance') - initial = kwargs.get('initial', {}) - - if instance is not None and instance.scope: - initial['scope'] = instance.scope - kwargs['initial'] = initial - - super().__init__(*args, **kwargs) - self._set_scoped_values() - - def clean(self): - super().clean() - - # Assign the selected scope (if any) - self.instance.scope = self.cleaned_data.get('scope') - class ClusterAddDevicesForm(forms.Form): region = DynamicModelChoiceField(