diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py index 4a86c55e7..261bb8bd6 100644 --- a/netbox/circuits/forms.py +++ b/netbox/circuits/forms.py @@ -113,7 +113,8 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -121,7 +122,8 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) asn = forms.IntegerField( required=False, @@ -198,7 +200,8 @@ class ProviderNetworkFilterForm(BootstrapMixin, CustomFieldModelFilterForm): provider_id = DynamicModelMultipleChoiceField( queryset=Provider.objects.all(), required=False, - label=_('Provider') + label=_('Provider'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -368,12 +371,14 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte type_id = DynamicModelMultipleChoiceField( queryset=CircuitType.objects.all(), required=False, - label=_('Type') + label=_('Type'), + fetch_trigger='open' ) provider_id = DynamicModelMultipleChoiceField( queryset=Provider.objects.all(), required=False, - label=_('Provider') + label=_('Provider'), + fetch_trigger='open' ) provider_network_id = DynamicModelMultipleChoiceField( queryset=ProviderNetwork.objects.all(), @@ -381,7 +386,8 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte query_params={ 'provider_id': '$provider_id' }, - label=_('Provider network') + label=_('Provider network'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=CircuitStatusChoices, @@ -391,7 +397,8 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -399,7 +406,8 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) commit_rate = forms.IntegerField( required=False, diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 0eb86035e..da9030ca9 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -71,12 +71,14 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -84,7 +86,8 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -92,7 +95,8 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Device') + label=_('Device'), + fetch_trigger='open' ) @@ -457,17 +461,19 @@ class SiteFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo status = forms.MultipleChoiceField( choices=SiteStatusChoices, required=False, - widget=StaticSelectMultiple() + widget=StaticSelectMultiple(), ) region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Group') + label=_('Group'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -565,7 +571,8 @@ class LocationFilterForm(BootstrapMixin, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -573,7 +580,8 @@ class LocationFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) parent_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -582,7 +590,8 @@ class LocationFilterForm(BootstrapMixin, CustomFieldModelFilterForm): 'region_id': '$region_id', 'site_id': '$site_id', }, - label=_('Parent') + label=_('Parent'), + fetch_trigger='open' ) @@ -862,7 +871,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -870,7 +880,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -879,7 +890,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo query_params={ 'site_id': '$site_id' }, - label=_('Location') + label=_('Location'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=RackStatusChoices, @@ -900,7 +912,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo queryset=RackRole.objects.all(), required=False, null_option='None', - label=_('Role') + label=_('Role'), + fetch_trigger='open' ) asset_tag = forms.CharField( required=False @@ -923,7 +936,8 @@ class RackElevationFilterForm(RackFilterForm): query_params={ 'site_id': '$site_id', 'location_id': '$location_id', - } + }, + fetch_trigger='open' ) @@ -937,14 +951,16 @@ class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): required=False, initial_params={ 'sites': '$site' - } + }, + fetch_trigger='open' ) site_group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' - } + }, + fetch_trigger='open' ) site = DynamicModelChoiceField( queryset=Site.objects.all(), @@ -952,21 +968,24 @@ class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): query_params={ 'region_id': '$region', 'group_id': '$site_group', - } + }, + fetch_trigger='open' ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' - } + }, + fetch_trigger='open' ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), query_params={ 'site_id': '$site', 'location_id': '$location', - } + }, + fetch_trigger='open' ) units = NumericArrayField( base_field=forms.IntegerField(), @@ -980,7 +999,8 @@ class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), - required=False + required=False, + fetch_trigger='open' ) class Meta: @@ -1080,7 +1100,8 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMo region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -1088,13 +1109,15 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMo query_params={ 'region_id': '$region_id' }, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.prefetch_related('site'), required=False, label=_('Location'), - null_option='None' + null_option='None', + fetch_trigger='open' ) user_id = DynamicModelMultipleChoiceField( queryset=User.objects.all(), @@ -1102,7 +1125,8 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMo label=_('User'), widget=APISelectMultiple( api_url='/api/users/users/', - ) + ), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -1231,7 +1255,8 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldModelFilterForm): manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer') + label=_('Manufacturer'), + fetch_trigger='open' ) subdevice_role = forms.MultipleChoiceField( choices=add_blank_choice(SubdeviceRoleChoices), @@ -2036,7 +2061,8 @@ class PlatformFilterForm(BootstrapMixin, CustomFieldModelFilterForm): manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer') + label=_('Manufacturer'), + fetch_trigger='open' ) @@ -2452,7 +2478,8 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -2460,7 +2487,8 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -2469,7 +2497,8 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt query_params={ 'site_id': '$site_id' }, - label=_('Location') + label=_('Location'), + fetch_trigger='open' ) rack_id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), @@ -2479,17 +2508,20 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt 'site_id': '$site_id', 'location_id': '$location_id', }, - label=_('Rack') + label=_('Rack'), + fetch_trigger='open' ) role_id = DynamicModelMultipleChoiceField( queryset=DeviceRole.objects.all(), required=False, - label=_('Role') + label=_('Role'), + fetch_trigger='open' ) manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer') + label=_('Manufacturer'), + fetch_trigger='open' ) device_type_id = DynamicModelMultipleChoiceField( queryset=DeviceType.objects.all(), @@ -2497,13 +2529,15 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt query_params={ 'manufacturer_id': '$manufacturer_id' }, - label=_('Model') + label=_('Model'), + fetch_trigger='open' ) platform_id = DynamicModelMultipleChoiceField( queryset=Platform.objects.all(), required=False, null_option='None', - label=_('Platform') + label=_('Platform'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=DeviceStatusChoices, @@ -3987,7 +4021,8 @@ class InventoryItemFilterForm(DeviceComponentFilterForm): manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer') + label=_('Manufacturer'), + fetch_trigger='open' ) serial = forms.CharField( required=False @@ -4461,7 +4496,8 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -4469,12 +4505,14 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) tenant_id = DynamicModelMultipleChoiceField( queryset=Tenant.objects.all(), required=False, - label=_('Tenant') + label=_('Tenant'), + fetch_trigger='open' ) rack_id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), @@ -4483,7 +4521,8 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm): null_option='None', query_params={ 'site_id': '$site_id' - } + }, + fetch_trigger='open' ) type = forms.MultipleChoiceField( choices=add_blank_choice(CableTypeChoices), @@ -4506,7 +4545,8 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm): 'tenant_id': '$tenant_id', 'rack_id': '$rack_id', }, - label=_('Device') + label=_('Device'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -4519,7 +4559,8 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -4527,7 +4568,8 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -4535,7 +4577,8 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form): query_params={ 'site_id': '$site_id' }, - label=_('Device') + label=_('Device'), + fetch_trigger='open' ) @@ -4543,7 +4586,8 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -4551,7 +4595,8 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -4559,7 +4604,8 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form): query_params={ 'site_id': '$site_id' }, - label=_('Device') + label=_('Device'), + fetch_trigger='open' ) @@ -4567,7 +4613,8 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -4575,7 +4622,8 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -4583,7 +4631,8 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form): query_params={ 'site_id': '$site_id' }, - label=_('Device') + label=_('Device'), + fetch_trigger='open' ) @@ -4837,12 +4886,14 @@ class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -4850,7 +4901,8 @@ class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -4973,12 +5025,14 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -4986,7 +5040,8 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -4995,7 +5050,8 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Location') + label=_('Location'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -5213,12 +5269,14 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -5226,7 +5284,8 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) power_panel_id = DynamicModelMultipleChoiceField( queryset=PowerPanel.objects.all(), @@ -5235,7 +5294,8 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Power panel') + label=_('Power panel'), + fetch_trigger='open' ) rack_id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), @@ -5244,7 +5304,8 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Rack') + label=_('Rack'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=PowerFeedStatusChoices, diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 3f3dead0e..16091885c 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -676,58 +676,69 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Regions') + label=_('Regions'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site groups') + label=_('Site groups'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), required=False, - label=_('Sites') + label=_('Sites'), + fetch_trigger='open' ) device_type_id = DynamicModelMultipleChoiceField( queryset=DeviceType.objects.all(), required=False, - label=_('Device types') + label=_('Device types'), + fetch_trigger='open' ) role_id = DynamicModelMultipleChoiceField( queryset=DeviceRole.objects.all(), required=False, - label=_('Roles') + label=_('Roles'), + fetch_trigger='open' ) platform_id = DynamicModelMultipleChoiceField( queryset=Platform.objects.all(), required=False, - label=_('Platforms') + label=_('Platforms'), + fetch_trigger='open' ) cluster_group_id = DynamicModelMultipleChoiceField( queryset=ClusterGroup.objects.all(), required=False, - label=_('Cluster groups') + label=_('Cluster groups'), + fetch_trigger='open' ) cluster_id = DynamicModelMultipleChoiceField( queryset=Cluster.objects.all(), required=False, - label=_('Clusters') + label=_('Clusters'), + fetch_trigger='open' ) tenant_group_id = DynamicModelMultipleChoiceField( queryset=TenantGroup.objects.all(), required=False, - label=_('Tenant groups') + label=_('Tenant groups'), + fetch_trigger='open' ) tenant_id = DynamicModelMultipleChoiceField( queryset=Tenant.objects.all(), required=False, - label=_('Tenant') + label=_('Tenant'), + fetch_trigger='open' ) tag = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), to_field_name='slug', required=False, - label=_('Tags') + label=_('Tags'), + fetch_trigger='open' ) @@ -820,7 +831,8 @@ class JournalEntryFilterForm(BootstrapMixin, forms.Form): label=_('User'), widget=APISelectMultiple( api_url='/api/users/users/', - ) + ), + fetch_trigger='open' ) assigned_object_type_id = DynamicModelMultipleChoiceField( queryset=ContentType.objects.all(), @@ -828,7 +840,8 @@ class JournalEntryFilterForm(BootstrapMixin, forms.Form): label=_('Object Type'), widget=APISelectMultiple( api_url='/api/extras/content-types/', - ) + ), + fetch_trigger='open' ) kind = forms.ChoiceField( choices=add_blank_choice(JournalEntryKindChoices), @@ -868,7 +881,8 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form): label=_('User'), widget=APISelectMultiple( api_url='/api/users/users/', - ) + ), + fetch_trigger='open' ) changed_object_type_id = DynamicModelMultipleChoiceField( queryset=ContentType.objects.all(), @@ -876,7 +890,8 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form): label=_('Object Type'), widget=APISelectMultiple( api_url='/api/extras/content-types/', - ) + ), + fetch_trigger='open' ) diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 2dc3171e6..f60853525 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -115,12 +115,14 @@ class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFor import_target_id = DynamicModelMultipleChoiceField( queryset=RouteTarget.objects.all(), required=False, - label=_('Import targets') + label=_('Import targets'), + fetch_trigger='open' ) export_target_id = DynamicModelMultipleChoiceField( queryset=RouteTarget.objects.all(), required=False, - label=_('Export targets') + label=_('Export targets'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -185,12 +187,14 @@ class RouteTargetFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelF importing_vrf_id = DynamicModelMultipleChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Imported by VRF') + label=_('Imported by VRF'), + fetch_trigger='open' ) exporting_vrf_id = DynamicModelMultipleChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Exported by VRF') + label=_('Exported by VRF'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -345,7 +349,8 @@ class AggregateFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFil rir_id = DynamicModelMultipleChoiceField( queryset=RIR.objects.all(), required=False, - label=_('RIR') + label=_('RIR'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -642,12 +647,14 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilter queryset=VRF.objects.all(), required=False, label=_('Assigned VRF'), - null_option='Global' + null_option='Global', + fetch_trigger='open' ) present_in_vrf_id = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Present in VRF') + label=_('Present in VRF'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=PrefixStatusChoices, @@ -657,12 +664,14 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilter region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -671,13 +680,15 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilter query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) role_id = DynamicModelMultipleChoiceField( queryset=Role.objects.all(), required=False, null_option='None', - label=_('Role') + label=_('Role'), + fetch_trigger='open' ) is_pool = forms.NullBooleanField( required=False, @@ -818,7 +829,8 @@ class IPRangeFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte queryset=VRF.objects.all(), required=False, label=_('Assigned VRF'), - null_option='Global' + null_option='Global', + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=PrefixStatusChoices, @@ -829,7 +841,8 @@ class IPRangeFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte queryset=Role.objects.all(), required=False, null_option='None', - label=_('Role') + label=_('Role'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -1265,12 +1278,14 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFil queryset=VRF.objects.all(), required=False, label=_('Assigned VRF'), - null_option='Global' + null_option='Global', + fetch_trigger='open' ) present_in_vrf_id = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Present in VRF') + label=_('Present in VRF'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=IPAddressStatusChoices, @@ -1439,27 +1454,32 @@ class VLANGroupFilterForm(BootstrapMixin, forms.Form): region = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) sitegroup = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), required=False, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) location = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), required=False, - label=_('Location') + label=_('Location'), + fetch_trigger='open' ) rack = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), required=False, - label=_('Rack') + label=_('Rack'), + fetch_trigger='open' ) @@ -1652,12 +1672,14 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -1666,7 +1688,8 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo query_params={ 'region': '$region' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) group_id = DynamicModelMultipleChoiceField( queryset=VLANGroup.objects.all(), @@ -1675,7 +1698,8 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo query_params={ 'region': '$region' }, - label=_('VLAN group') + label=_('VLAN group'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=VLANStatusChoices, @@ -1686,7 +1710,8 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo queryset=Role.objects.all(), required=False, null_option='None', - label=_('Role') + label=_('Role'), + fetch_trigger='open' ) tag = TagFilterField(model) diff --git a/netbox/project-static/dist/netbox-dark.css b/netbox/project-static/dist/netbox-dark.css index ca9ac8d6a..8e84ace00 100644 Binary files a/netbox/project-static/dist/netbox-dark.css and b/netbox/project-static/dist/netbox-dark.css differ diff --git a/netbox/project-static/dist/netbox-light.css b/netbox/project-static/dist/netbox-light.css index 423470886..b657366d7 100644 Binary files a/netbox/project-static/dist/netbox-light.css and b/netbox/project-static/dist/netbox-light.css differ diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 93f8ecf32..0fed51635 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 197b62541..cd6158188 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/src/select/api.ts b/netbox/project-static/src/select/api.ts index e9199603d..dc1a9f1a5 100644 --- a/netbox/project-static/src/select/api.ts +++ b/netbox/project-static/src/select/api.ts @@ -2,7 +2,7 @@ import queryString from 'query-string'; import { readableColor } from 'color2k'; import SlimSelect from 'slim-select'; import { createToast } from '../bs'; -import { hasUrl, hasExclusions } from './util'; +import { hasUrl, hasExclusions, isTrigger } from './util'; import { isTruthy, hasError, @@ -10,6 +10,7 @@ import { getApiData, isApiError, getElements, + createElement, findFirstAdjacent, } from '../util'; @@ -17,6 +18,20 @@ import type { Option } from 'slim-select/dist/data'; type QueryFilter = Map; +export type Trigger = + /** + * Load data when the select element is opened. + */ + | 'open' + /** + * Load data when the element is loaded. + */ + | 'load' + /** + * Load data when a parent element is uncollapsed. + */ + | 'collapse'; + // Various one-off patterns to replace in query param keys. const REPLACE_PATTERNS = [ // Don't query `termination_a_device=1`, but rather `device=1`. @@ -57,6 +72,17 @@ class APISelect { */ public readonly placeholder: string; + /** + * Event that will initiate the API call to NetBox to load option data. By default, the trigger + * is `'load'`, so data will be fetched when the element renders on the page. + */ + private readonly trigger: Trigger; + + /** + * If `true`, a refresh button will be added next to the search/filter `` element. + */ + private readonly allowRefresh: boolean = true; + /** * Event to be dispatched when dependent fields' values change. */ @@ -153,6 +179,7 @@ class APISelect { allowDeselect: true, deselectLabel: ``, placeholder: this.placeholder, + searchPlaceholder: 'Filter', onChange: () => this.handleSlimChange(), }); @@ -186,20 +213,44 @@ class APISelect { // Initialize controlling elements. this.initResetButton(); + // Add the refresh button to the search element. + this.initRefreshButton(); + // Add dependency event listeners. this.addEventListeners(); + // Determine if the fetch trigger has been set. + const triggerAttr = this.base.getAttribute('data-fetch-trigger'); + // Determine if this element is part of collapsible element. const collapse = this.base.closest('.content-container .collapse'); - if (collapse !== null) { - // If this element is part of a collapsible element, only load the data when the - // collapsible element is shown. - // See: https://getbootstrap.com/docs/5.0/components/collapse/#events - collapse.addEventListener('show.bs.collapse', () => this.loadData()); - collapse.addEventListener('hide.bs.collapse', () => this.resetOptions()); + + if (isTrigger(triggerAttr)) { + this.trigger = triggerAttr; + } else if (collapse !== null) { + this.trigger = 'collapse'; } else { - // Otherwise, load the data on render. - Promise.all([this.loadData()]); + this.trigger = 'load'; + } + + switch (this.trigger) { + case 'collapse': + if (collapse !== null) { + // If this element is part of a collapsible element, only load the data when the + // collapsible element is shown. + // See: https://getbootstrap.com/docs/5.0/components/collapse/#events + collapse.addEventListener('show.bs.collapse', () => this.loadData()); + collapse.addEventListener('hide.bs.collapse', () => this.resetOptions()); + } + break; + case 'open': + // If the trigger is 'open', only load API data when the select element is opened. + this.slim.beforeOpen = () => this.loadData(); + break; + case 'load': + // Otherwise, load the data immediately. + Promise.all([this.loadData()]); + break; } } @@ -713,21 +764,37 @@ class APISelect { } /** - * Initialize any adjacent reset buttons so that when clicked, the instance's selected value is cleared. + * Initialize any adjacent reset buttons so that when clicked, the page is reloaded without + * query parameters. */ private initResetButton(): void { - const resetButton = findFirstAdjacent(this.base, 'button[data-reset-select'); + const resetButton = findFirstAdjacent( + this.base, + 'button[data-reset-select]', + ); if (resetButton !== null) { resetButton.addEventListener('click', () => { - this.base.value = ''; - if (this.base.multiple) { - this.slim.setSelected([]); - } else { - this.slim.setSelected(''); - } + window.location.assign(window.location.origin + window.location.pathname); }); } } + + /** + * Add a refresh button to the search container element. When clicked, the API data will be + * reloaded. + */ + private initRefreshButton(): void { + if (this.allowRefresh) { + const refreshButton = createElement( + 'button', + { type: 'button' }, + ['btn', 'btn-sm', 'btn-ghost-dark'], + [createElement('i', {}, ['mdi', 'mdi-reload'])], + ); + refreshButton.addEventListener('click', () => this.loadData()); + this.slim.slim.search.container.appendChild(refreshButton); + } + } } export function initApiSelect() { diff --git a/netbox/project-static/src/select/util.ts b/netbox/project-static/src/select/util.ts index e79a233fc..daf7839dc 100644 --- a/netbox/project-static/src/select/util.ts +++ b/netbox/project-static/src/select/util.ts @@ -1,3 +1,5 @@ +import type { Trigger } from './api'; + /** * Determine if an element has the `data-url` attribute set. */ @@ -15,3 +17,10 @@ export function hasExclusions( const exclude = el.getAttribute('data-query-param-exclude'); return typeof exclude === 'string' && exclude !== ''; } + +/** + * Determine if a trigger value is valid. + */ +export function isTrigger(value: unknown): value is Trigger { + return typeof value === 'string' && ['load', 'open', 'collapse'].includes(value); +} diff --git a/netbox/project-static/styles/netbox.scss b/netbox/project-static/styles/netbox.scss index 5f395ff7c..6392a03ca 100644 --- a/netbox/project-static/styles/netbox.scss +++ b/netbox/project-static/styles/netbox.scss @@ -52,7 +52,7 @@ } * { - transition: $transition-100ms-ease-in-out; + transition: background-color, color 0.1s ease-in-out; } .mw-25 { @@ -302,8 +302,13 @@ span.profile-button .dropdown-menu { } } -div#advanced-search-content div.card div.card-body div.col:not(:last-child) { - margin-right: 1rem; +div#advanced-search-content { + &.collapsing { + transition: height 0.1s ease-in-out; + } + div.card div.card-body div.col:not(:last-child) { + margin-right: 1rem; + } } body { @@ -430,6 +435,7 @@ nav.search { background-color: var(--nbx-body-bg); // Don't overtake dropdowns z-index: 999; + justify-content: center; form button.dropdown-toggle { border-color: $input-border-color; font-weight: $input-group-addon-font-weight; diff --git a/netbox/project-static/styles/select.scss b/netbox/project-static/styles/select.scss index bc51cb4ea..8b9c51865 100644 --- a/netbox/project-static/styles/select.scss +++ b/netbox/project-static/styles/select.scss @@ -71,8 +71,8 @@ $spacing-s: $input-padding-x; border-color: currentColor; } } + // Don't show the depth indicator outside of the menu. .placeholder .depth { - // Don't show the depth indicator outside of the menu. display: none; } span.placeholder > *, @@ -94,6 +94,11 @@ $spacing-s: $input-padding-x; .ss-value { border-radius: $badge-border-radius; color: var(--nbx-select-value-color); + + // Don't show the depth indicator outside of the menu. + .depth { + display: none; + } } } .ss-add { @@ -133,10 +138,34 @@ $spacing-s: $input-padding-x; opacity: 0.3; } } + &::-webkit-scrollbar { + right: 0; + width: 4px; + &:hover { + opacity: 0.8; + } + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + right: 0; + width: 2px; + background-color: var(--nbx-sidebar-scroll); + } } border-bottom-left-radius: $form-select-border-radius; border-bottom-right-radius: $form-select-border-radius; + .ss-search { + padding-right: $spacer * 0.5; + + button { + margin-left: $spacer * 0.75; + } + input[type='search'] { background-color: $form-select-bg; color: $input-color; diff --git a/netbox/templates/dcim/connections_list.html b/netbox/templates/dcim/connections_list.html index f590c4199..cfe17d62f 100644 --- a/netbox/templates/dcim/connections_list.html +++ b/netbox/templates/dcim/connections_list.html @@ -1,42 +1,24 @@ {% extends 'base/layout.html' %} {% load buttons %} +{% load render_table from django_tables2 %} {% block title %}{{ title }}{% endblock %} {% block extra_controls %}{% export_button content_type %}{% endblock %} {% block content %} -{% if filter_form %} -
- {% include 'inc/advanced_search.html' %} -
-{% endif %} -
-
-
-
-
-
-
- - {% if filter_form %} - - {% endif %} -
-
-
-
-
- {% include 'inc/responsive_table.html' %} +
+
+ {% include 'inc/table_controls.html' %} + +
+ {% render_table table 'inc/table.html' %}
+ + {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
- {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} + {% if filter_form %} + {% include 'inc/filter_list.html' %} + {% endif %}
-
{% endblock %} diff --git a/netbox/templates/dcim/rack_elevation_list.html b/netbox/templates/dcim/rack_elevation_list.html index 0e2fbfdad..55e1eeacc 100644 --- a/netbox/templates/dcim/rack_elevation_list.html +++ b/netbox/templates/dcim/rack_elevation_list.html @@ -5,63 +5,58 @@ {% block title %}Rack Elevations{% endblock %} {% block controls %} -
-
- - -
- Front - Rear -
-
- Normal - Reversed +
+
+ +
+ Front + Rear +
+
-
{% endblock %} {% block content %} -
- {% include 'inc/advanced_search.html' %} -
-
-
- {% if page %} -
- {% for rack in page %} -
-
-
- {{ rack.name }} - {% if rack.role %} -
{{ rack.role }} - {% endif %} - {% if rack.facility_id %} -
{{ rack.facility_id }} - {% endif %} +
+
+ {% if page %} +
+ {% for rack in page %} +
+
+
+ {{ rack.name }} + {% if rack.role %} +
{{ rack.role }} + {% endif %} + {% if rack.facility_id %} +
{{ rack.facility_id }} + {% endif %} +
+ {% include 'dcim/inc/rack_elevation.html' with object=rack face=rack_face %} +
+
+ {{ rack.name }} + {% if rack.facility_id %} + ({{ rack.facility_id }}) + {% endif %} +
- {% include 'dcim/inc/rack_elevation.html' with object=rack face=rack_face %} -
-
- {{ rack.name }} - {% if rack.facility_id %} - ({{ rack.facility_id }}) - {% endif %}
-
-
- {% endfor %} -
-
- {% include 'inc/paginator.html' %} - {% else %} -

No Racks Found

- {% endif %} + {% endfor %} +
+
+ {% include 'inc/paginator.html' %} + {% else %} +

No Racks Found

+ {% endif %} +
+ {% include 'inc/filter_list.html' %}
-
{% endblock %} diff --git a/netbox/templates/generic/object_list.html b/netbox/templates/generic/object_list.html index e6ae37403..9ac484227 100644 --- a/netbox/templates/generic/object_list.html +++ b/netbox/templates/generic/object_list.html @@ -24,9 +24,6 @@ {% endblock controls %} {% block content %} -{% if filter_form %} - {% include 'inc/advanced_search.html' %} -{% endif %} {% if table.paginator.num_pages > 1 %} {% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %}
@@ -57,12 +54,12 @@ {% endwith %} {% endif %} -{# Object list filter, table config #} -{% include 'inc/table_controls.html' with table_modal="ObjectTable_config" %} - {# Object table #}
-
+
+ {# Object list filter, table config #} + {% include 'inc/table_controls.html' with table_modal="ObjectTable_config" %} + {% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %} {% if permissions.change or permissions.delete %}
@@ -95,6 +92,9 @@ {% endwith %} {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
+ {% if filter_form %} + {% include 'inc/filter_list.html' %} + {% endif %}
{% table_config_form table table_name="ObjectTable" %} {% endblock content %} diff --git a/netbox/templates/inc/filter_list.html b/netbox/templates/inc/filter_list.html new file mode 100644 index 000000000..07aa2249b --- /dev/null +++ b/netbox/templates/inc/filter_list.html @@ -0,0 +1,62 @@ +{% load form_helpers %} +{% load helpers %} + +
+ +
+
+ Field Filters +
+
+ {% for field in filter_form.hidden_fields %} + {{ field }} + {% endfor %} + {% if filter_form.field_groups %} + {% for group in filter_form.field_groups %} +
+ {% for name in group %} + {% with field=filter_form|get_item:name %} + {% if field|widget_type == 'checkboxinput' %} +
+ + {{ field }} +
+ {% else %} +
+ + {{ field }} +
+ {% endif %} + {% endwith %} + {% endfor %} +
+ {% endfor %} + {% else %} + {% for field in filter_form.visible_fields %} +
+ {% if field|widget_type == 'checkboxinput' %} +
+ + {{ field }} +
+ {% else %} +
+ + {{ field }} +
+ {% endif %} +
+ {% endfor %} + {% endif %} +
+ +
+ +
diff --git a/netbox/templates/inc/table_controls.html b/netbox/templates/inc/table_controls.html index bf604ab27..ec46cd535 100644 --- a/netbox/templates/inc/table_controls.html +++ b/netbox/templates/inc/table_controls.html @@ -1,6 +1,6 @@
- {% if request.user.is_authenticated %} + {% if request.user.is_authenticated and table_modal %}
- {% endif %}
diff --git a/netbox/tenancy/forms.py b/netbox/tenancy/forms.py index 19e2fc38c..05b47ad4c 100644 --- a/netbox/tenancy/forms.py +++ b/netbox/tenancy/forms.py @@ -67,7 +67,8 @@ class TenantGroupFilterForm(BootstrapMixin, CustomFieldModelFilterForm): parent_id = DynamicModelMultipleChoiceField( queryset=TenantGroup.objects.all(), required=False, - label=_('Parent group') + label=_('Parent group'), + fetch_trigger='open' ) @@ -137,7 +138,8 @@ class TenantFilterForm(BootstrapMixin, CustomFieldModelFilterForm): queryset=TenantGroup.objects.all(), required=False, null_option='None', - label=_('Group') + label=_('Group'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -169,7 +171,8 @@ class TenancyFilterForm(forms.Form): queryset=TenantGroup.objects.all(), required=False, null_option='None', - label=_('Tenant group') + label=_('Tenant group'), + fetch_trigger='open' ) tenant_id = DynamicModelMultipleChoiceField( queryset=Tenant.objects.all(), @@ -178,5 +181,6 @@ class TenancyFilterForm(forms.Form): query_params={ 'group_id': '$tenant_group_id' }, - label=_('Tenant') + label=_('Tenant'), + fetch_trigger='open' ) diff --git a/netbox/utilities/forms/fields.py b/netbox/utilities/forms/fields.py index b1db79ecc..9ea4ddb81 100644 --- a/netbox/utilities/forms/fields.py +++ b/netbox/utilities/forms/fields.py @@ -363,16 +363,19 @@ class DynamicModelChoiceMixin: :param null_option: The string used to represent a null selection (if any) :param disabled_indicator: The name of the field which, if populated, will disable selection of the choice (optional) + :param str fetch_trigger: The event type which will cause the select element to + fetch data from the API. Must be 'load', 'open', or 'collapse'. (optional) """ filter = django_filters.ModelChoiceFilter widget = widgets.APISelect - def __init__(self, query_params=None, initial_params=None, null_option=None, disabled_indicator=None, *args, + def __init__(self, query_params=None, initial_params=None, null_option=None, disabled_indicator=None, fetch_trigger=None, *args, **kwargs): self.query_params = query_params or {} self.initial_params = initial_params or {} self.null_option = null_option self.disabled_indicator = disabled_indicator + self.fetch_trigger = fetch_trigger # to_field_name is set by ModelChoiceField.__init__(), but we need to set it early for reference # by widget_attrs() @@ -394,6 +397,10 @@ class DynamicModelChoiceMixin: # Set the disabled indicator, if any if self.disabled_indicator is not None: attrs['disabled-indicator'] = self.disabled_indicator + + # Set the fetch trigger, if any. + if self.fetch_trigger is not None: + attrs['data-fetch-trigger'] = self.fetch_trigger # Attach any static query parameters for key, value in self.query_params.items(): diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index 332bebe8e..7cef7434b 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -236,12 +236,14 @@ class ClusterFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte type_id = DynamicModelMultipleChoiceField( queryset=ClusterType.objects.all(), required=False, - label=_('Type') + label=_('Type'), + fetch_trigger='open' ) region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -250,13 +252,15 @@ class ClusterFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte query_params={ 'region_id': '$region_id' }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) group_id = DynamicModelMultipleChoiceField( queryset=ClusterGroup.objects.all(), required=False, null_option='None', - label=_('Group') + label=_('Group'), + fetch_trigger='open' ) tag = TagFilterField(model) @@ -547,28 +551,33 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod queryset=ClusterGroup.objects.all(), required=False, null_option='None', - label=_('Cluster group') + label=_('Cluster group'), + fetch_trigger='open' ) cluster_type_id = DynamicModelMultipleChoiceField( queryset=ClusterType.objects.all(), required=False, null_option='None', - label=_('Cluster type') + label=_('Cluster type'), + fetch_trigger='open' ) cluster_id = DynamicModelMultipleChoiceField( queryset=Cluster.objects.all(), required=False, - label=_('Cluster') + label=_('Cluster'), + fetch_trigger='open' ) region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region') + label=_('Region'), + fetch_trigger='open' ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group') + label=_('Site group'), + fetch_trigger='open' ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -578,7 +587,8 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod 'region_id': '$region_id', 'group_id': '$site_group_id', }, - label=_('Site') + label=_('Site'), + fetch_trigger='open' ) role_id = DynamicModelMultipleChoiceField( queryset=DeviceRole.objects.all(), @@ -587,7 +597,8 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod query_params={ 'vm_role': "True" }, - label=_('Role') + label=_('Role'), + fetch_trigger='open' ) status = forms.MultipleChoiceField( choices=VirtualMachineStatusChoices, @@ -598,7 +609,8 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod queryset=Platform.objects.all(), required=False, null_option='None', - label=_('Platform') + label=_('Platform'), + fetch_trigger='open' ) mac_address = forms.CharField( required=False, @@ -850,7 +862,8 @@ class VMInterfaceFilterForm(BootstrapMixin, forms.Form): cluster_id = DynamicModelMultipleChoiceField( queryset=Cluster.objects.all(), required=False, - label=_('Cluster') + label=_('Cluster'), + fetch_trigger='open' ) virtual_machine_id = DynamicModelMultipleChoiceField( queryset=VirtualMachine.objects.all(), @@ -858,7 +871,8 @@ class VMInterfaceFilterForm(BootstrapMixin, forms.Form): query_params={ 'cluster_id': '$cluster_id' }, - label=_('Virtual machine') + label=_('Virtual machine'), + fetch_trigger='open' ) enabled = forms.NullBooleanField( required=False,