Deprecate collapsible advanced search and re-implement field-based filtering on object views

This commit is contained in:
checktheroads 2021-08-01 21:24:22 -07:00
parent 0b09365d0d
commit 863048cda2
20 changed files with 534 additions and 260 deletions

View File

@ -113,7 +113,8 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -121,7 +122,8 @@ class ProviderFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
asn = forms.IntegerField( asn = forms.IntegerField(
required=False, required=False,
@ -198,7 +200,8 @@ class ProviderNetworkFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
provider_id = DynamicModelMultipleChoiceField( provider_id = DynamicModelMultipleChoiceField(
queryset=Provider.objects.all(), queryset=Provider.objects.all(),
required=False, required=False,
label=_('Provider') label=_('Provider'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -368,12 +371,14 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
type_id = DynamicModelMultipleChoiceField( type_id = DynamicModelMultipleChoiceField(
queryset=CircuitType.objects.all(), queryset=CircuitType.objects.all(),
required=False, required=False,
label=_('Type') label=_('Type'),
fetch_trigger='open'
) )
provider_id = DynamicModelMultipleChoiceField( provider_id = DynamicModelMultipleChoiceField(
queryset=Provider.objects.all(), queryset=Provider.objects.all(),
required=False, required=False,
label=_('Provider') label=_('Provider'),
fetch_trigger='open'
) )
provider_network_id = DynamicModelMultipleChoiceField( provider_network_id = DynamicModelMultipleChoiceField(
queryset=ProviderNetwork.objects.all(), queryset=ProviderNetwork.objects.all(),
@ -381,7 +386,8 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
query_params={ query_params={
'provider_id': '$provider_id' 'provider_id': '$provider_id'
}, },
label=_('Provider network') label=_('Provider network'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=CircuitStatusChoices, choices=CircuitStatusChoices,
@ -391,7 +397,8 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -399,7 +406,8 @@ class CircuitFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
commit_rate = forms.IntegerField( commit_rate = forms.IntegerField(
required=False, required=False,

View File

@ -71,12 +71,14 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -84,7 +86,8 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
device_id = DynamicModelMultipleChoiceField( device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),
@ -92,7 +95,8 @@ class DeviceComponentFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Device') label=_('Device'),
fetch_trigger='open'
) )
@ -457,17 +461,19 @@ class SiteFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=SiteStatusChoices, choices=SiteStatusChoices,
required=False, required=False,
widget=StaticSelectMultiple() widget=StaticSelectMultiple(),
) )
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
group_id = DynamicModelMultipleChoiceField( group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Group') label=_('Group'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -565,7 +571,8 @@ class LocationFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -573,7 +580,8 @@ class LocationFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -582,7 +590,8 @@ class LocationFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
'region_id': '$region_id', 'region_id': '$region_id',
'site_id': '$site_id', 'site_id': '$site_id',
}, },
label=_('Parent') label=_('Parent'),
fetch_trigger='open'
) )
@ -862,7 +871,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -870,7 +880,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
location_id = DynamicModelMultipleChoiceField( location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -879,7 +890,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Location') label=_('Location'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=RackStatusChoices, choices=RackStatusChoices,
@ -900,7 +912,8 @@ class RackFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
queryset=RackRole.objects.all(), queryset=RackRole.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Role') label=_('Role'),
fetch_trigger='open'
) )
asset_tag = forms.CharField( asset_tag = forms.CharField(
required=False required=False
@ -923,7 +936,8 @@ class RackElevationFilterForm(RackFilterForm):
query_params={ query_params={
'site_id': '$site_id', 'site_id': '$site_id',
'location_id': '$location_id', 'location_id': '$location_id',
} },
fetch_trigger='open'
) )
@ -937,14 +951,16 @@ class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
required=False, required=False,
initial_params={ initial_params={
'sites': '$site' 'sites': '$site'
} },
fetch_trigger='open'
) )
site_group = DynamicModelChoiceField( site_group = DynamicModelChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
initial_params={ initial_params={
'sites': '$site' 'sites': '$site'
} },
fetch_trigger='open'
) )
site = DynamicModelChoiceField( site = DynamicModelChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -952,21 +968,24 @@ class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
query_params={ query_params={
'region_id': '$region', 'region_id': '$region',
'group_id': '$site_group', 'group_id': '$site_group',
} },
fetch_trigger='open'
) )
location = DynamicModelChoiceField( location = DynamicModelChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
required=False, required=False,
query_params={ query_params={
'site_id': '$site' 'site_id': '$site'
} },
fetch_trigger='open'
) )
rack = DynamicModelChoiceField( rack = DynamicModelChoiceField(
queryset=Rack.objects.all(), queryset=Rack.objects.all(),
query_params={ query_params={
'site_id': '$site', 'site_id': '$site',
'location_id': '$location', 'location_id': '$location',
} },
fetch_trigger='open'
) )
units = NumericArrayField( units = NumericArrayField(
base_field=forms.IntegerField(), base_field=forms.IntegerField(),
@ -980,7 +999,8 @@ class RackReservationForm(BootstrapMixin, TenancyForm, CustomFieldModelForm):
) )
tags = DynamicModelMultipleChoiceField( tags = DynamicModelMultipleChoiceField(
queryset=Tag.objects.all(), queryset=Tag.objects.all(),
required=False required=False,
fetch_trigger='open'
) )
class Meta: class Meta:
@ -1080,7 +1100,8 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMo
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -1088,13 +1109,15 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMo
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
location_id = DynamicModelMultipleChoiceField( location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.prefetch_related('site'), queryset=Location.objects.prefetch_related('site'),
required=False, required=False,
label=_('Location'), label=_('Location'),
null_option='None' null_option='None',
fetch_trigger='open'
) )
user_id = DynamicModelMultipleChoiceField( user_id = DynamicModelMultipleChoiceField(
queryset=User.objects.all(), queryset=User.objects.all(),
@ -1102,7 +1125,8 @@ class RackReservationFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMo
label=_('User'), label=_('User'),
widget=APISelectMultiple( widget=APISelectMultiple(
api_url='/api/users/users/', api_url='/api/users/users/',
) ),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -1231,7 +1255,8 @@ class DeviceTypeFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
manufacturer_id = DynamicModelMultipleChoiceField( manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(), queryset=Manufacturer.objects.all(),
required=False, required=False,
label=_('Manufacturer') label=_('Manufacturer'),
fetch_trigger='open'
) )
subdevice_role = forms.MultipleChoiceField( subdevice_role = forms.MultipleChoiceField(
choices=add_blank_choice(SubdeviceRoleChoices), choices=add_blank_choice(SubdeviceRoleChoices),
@ -2036,7 +2061,8 @@ class PlatformFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
manufacturer_id = DynamicModelMultipleChoiceField( manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(), queryset=Manufacturer.objects.all(),
required=False, required=False,
label=_('Manufacturer') label=_('Manufacturer'),
fetch_trigger='open'
) )
@ -2452,7 +2478,8 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -2460,7 +2487,8 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
location_id = DynamicModelMultipleChoiceField( location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -2469,7 +2497,8 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Location') label=_('Location'),
fetch_trigger='open'
) )
rack_id = DynamicModelMultipleChoiceField( rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(), queryset=Rack.objects.all(),
@ -2479,17 +2508,20 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
'site_id': '$site_id', 'site_id': '$site_id',
'location_id': '$location_id', 'location_id': '$location_id',
}, },
label=_('Rack') label=_('Rack'),
fetch_trigger='open'
) )
role_id = DynamicModelMultipleChoiceField( role_id = DynamicModelMultipleChoiceField(
queryset=DeviceRole.objects.all(), queryset=DeviceRole.objects.all(),
required=False, required=False,
label=_('Role') label=_('Role'),
fetch_trigger='open'
) )
manufacturer_id = DynamicModelMultipleChoiceField( manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(), queryset=Manufacturer.objects.all(),
required=False, required=False,
label=_('Manufacturer') label=_('Manufacturer'),
fetch_trigger='open'
) )
device_type_id = DynamicModelMultipleChoiceField( device_type_id = DynamicModelMultipleChoiceField(
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),
@ -2497,13 +2529,15 @@ class DeviceFilterForm(BootstrapMixin, LocalConfigContextFilterForm, TenancyFilt
query_params={ query_params={
'manufacturer_id': '$manufacturer_id' 'manufacturer_id': '$manufacturer_id'
}, },
label=_('Model') label=_('Model'),
fetch_trigger='open'
) )
platform_id = DynamicModelMultipleChoiceField( platform_id = DynamicModelMultipleChoiceField(
queryset=Platform.objects.all(), queryset=Platform.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Platform') label=_('Platform'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=DeviceStatusChoices, choices=DeviceStatusChoices,
@ -3987,7 +4021,8 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
manufacturer_id = DynamicModelMultipleChoiceField( manufacturer_id = DynamicModelMultipleChoiceField(
queryset=Manufacturer.objects.all(), queryset=Manufacturer.objects.all(),
required=False, required=False,
label=_('Manufacturer') label=_('Manufacturer'),
fetch_trigger='open'
) )
serial = forms.CharField( serial = forms.CharField(
required=False required=False
@ -4461,7 +4496,8 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -4469,12 +4505,14 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
tenant_id = DynamicModelMultipleChoiceField( tenant_id = DynamicModelMultipleChoiceField(
queryset=Tenant.objects.all(), queryset=Tenant.objects.all(),
required=False, required=False,
label=_('Tenant') label=_('Tenant'),
fetch_trigger='open'
) )
rack_id = DynamicModelMultipleChoiceField( rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(), queryset=Rack.objects.all(),
@ -4483,7 +4521,8 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
null_option='None', null_option='None',
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
} },
fetch_trigger='open'
) )
type = forms.MultipleChoiceField( type = forms.MultipleChoiceField(
choices=add_blank_choice(CableTypeChoices), choices=add_blank_choice(CableTypeChoices),
@ -4506,7 +4545,8 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
'tenant_id': '$tenant_id', 'tenant_id': '$tenant_id',
'rack_id': '$rack_id', 'rack_id': '$rack_id',
}, },
label=_('Device') label=_('Device'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -4519,7 +4559,8 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -4527,7 +4568,8 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
device_id = DynamicModelMultipleChoiceField( device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),
@ -4535,7 +4577,8 @@ class ConsoleConnectionFilterForm(BootstrapMixin, forms.Form):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Device') label=_('Device'),
fetch_trigger='open'
) )
@ -4543,7 +4586,8 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -4551,7 +4595,8 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
device_id = DynamicModelMultipleChoiceField( device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),
@ -4559,7 +4604,8 @@ class PowerConnectionFilterForm(BootstrapMixin, forms.Form):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Device') label=_('Device'),
fetch_trigger='open'
) )
@ -4567,7 +4613,8 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -4575,7 +4622,8 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
device_id = DynamicModelMultipleChoiceField( device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),
@ -4583,7 +4631,8 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Device') label=_('Device'),
fetch_trigger='open'
) )
@ -4837,12 +4886,14 @@ class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -4850,7 +4901,8 @@ class VirtualChassisFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -4973,12 +5025,14 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -4986,7 +5040,8 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
location_id = DynamicModelMultipleChoiceField( location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -4995,7 +5050,8 @@ class PowerPanelFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Location') label=_('Location'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -5213,12 +5269,14 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -5226,7 +5284,8 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
power_panel_id = DynamicModelMultipleChoiceField( power_panel_id = DynamicModelMultipleChoiceField(
queryset=PowerPanel.objects.all(), queryset=PowerPanel.objects.all(),
@ -5235,7 +5294,8 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Power panel') label=_('Power panel'),
fetch_trigger='open'
) )
rack_id = DynamicModelMultipleChoiceField( rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(), queryset=Rack.objects.all(),
@ -5244,7 +5304,8 @@ class PowerFeedFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Rack') label=_('Rack'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=PowerFeedStatusChoices, choices=PowerFeedStatusChoices,

View File

@ -676,58 +676,69 @@ class ConfigContextFilterForm(BootstrapMixin, forms.Form):
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Regions') label=_('Regions'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site groups') label=_('Site groups'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
required=False, required=False,
label=_('Sites') label=_('Sites'),
fetch_trigger='open'
) )
device_type_id = DynamicModelMultipleChoiceField( device_type_id = DynamicModelMultipleChoiceField(
queryset=DeviceType.objects.all(), queryset=DeviceType.objects.all(),
required=False, required=False,
label=_('Device types') label=_('Device types'),
fetch_trigger='open'
) )
role_id = DynamicModelMultipleChoiceField( role_id = DynamicModelMultipleChoiceField(
queryset=DeviceRole.objects.all(), queryset=DeviceRole.objects.all(),
required=False, required=False,
label=_('Roles') label=_('Roles'),
fetch_trigger='open'
) )
platform_id = DynamicModelMultipleChoiceField( platform_id = DynamicModelMultipleChoiceField(
queryset=Platform.objects.all(), queryset=Platform.objects.all(),
required=False, required=False,
label=_('Platforms') label=_('Platforms'),
fetch_trigger='open'
) )
cluster_group_id = DynamicModelMultipleChoiceField( cluster_group_id = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(), queryset=ClusterGroup.objects.all(),
required=False, required=False,
label=_('Cluster groups') label=_('Cluster groups'),
fetch_trigger='open'
) )
cluster_id = DynamicModelMultipleChoiceField( cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(), queryset=Cluster.objects.all(),
required=False, required=False,
label=_('Clusters') label=_('Clusters'),
fetch_trigger='open'
) )
tenant_group_id = DynamicModelMultipleChoiceField( tenant_group_id = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(), queryset=TenantGroup.objects.all(),
required=False, required=False,
label=_('Tenant groups') label=_('Tenant groups'),
fetch_trigger='open'
) )
tenant_id = DynamicModelMultipleChoiceField( tenant_id = DynamicModelMultipleChoiceField(
queryset=Tenant.objects.all(), queryset=Tenant.objects.all(),
required=False, required=False,
label=_('Tenant') label=_('Tenant'),
fetch_trigger='open'
) )
tag = DynamicModelMultipleChoiceField( tag = DynamicModelMultipleChoiceField(
queryset=Tag.objects.all(), queryset=Tag.objects.all(),
to_field_name='slug', to_field_name='slug',
required=False, required=False,
label=_('Tags') label=_('Tags'),
fetch_trigger='open'
) )
@ -820,7 +831,8 @@ class JournalEntryFilterForm(BootstrapMixin, forms.Form):
label=_('User'), label=_('User'),
widget=APISelectMultiple( widget=APISelectMultiple(
api_url='/api/users/users/', api_url='/api/users/users/',
) ),
fetch_trigger='open'
) )
assigned_object_type_id = DynamicModelMultipleChoiceField( assigned_object_type_id = DynamicModelMultipleChoiceField(
queryset=ContentType.objects.all(), queryset=ContentType.objects.all(),
@ -828,7 +840,8 @@ class JournalEntryFilterForm(BootstrapMixin, forms.Form):
label=_('Object Type'), label=_('Object Type'),
widget=APISelectMultiple( widget=APISelectMultiple(
api_url='/api/extras/content-types/', api_url='/api/extras/content-types/',
) ),
fetch_trigger='open'
) )
kind = forms.ChoiceField( kind = forms.ChoiceField(
choices=add_blank_choice(JournalEntryKindChoices), choices=add_blank_choice(JournalEntryKindChoices),
@ -868,7 +881,8 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
label=_('User'), label=_('User'),
widget=APISelectMultiple( widget=APISelectMultiple(
api_url='/api/users/users/', api_url='/api/users/users/',
) ),
fetch_trigger='open'
) )
changed_object_type_id = DynamicModelMultipleChoiceField( changed_object_type_id = DynamicModelMultipleChoiceField(
queryset=ContentType.objects.all(), queryset=ContentType.objects.all(),
@ -876,7 +890,8 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
label=_('Object Type'), label=_('Object Type'),
widget=APISelectMultiple( widget=APISelectMultiple(
api_url='/api/extras/content-types/', api_url='/api/extras/content-types/',
) ),
fetch_trigger='open'
) )

View File

@ -115,12 +115,14 @@ class VRFFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFor
import_target_id = DynamicModelMultipleChoiceField( import_target_id = DynamicModelMultipleChoiceField(
queryset=RouteTarget.objects.all(), queryset=RouteTarget.objects.all(),
required=False, required=False,
label=_('Import targets') label=_('Import targets'),
fetch_trigger='open'
) )
export_target_id = DynamicModelMultipleChoiceField( export_target_id = DynamicModelMultipleChoiceField(
queryset=RouteTarget.objects.all(), queryset=RouteTarget.objects.all(),
required=False, required=False,
label=_('Export targets') label=_('Export targets'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -185,12 +187,14 @@ class RouteTargetFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelF
importing_vrf_id = DynamicModelMultipleChoiceField( importing_vrf_id = DynamicModelMultipleChoiceField(
queryset=VRF.objects.all(), queryset=VRF.objects.all(),
required=False, required=False,
label=_('Imported by VRF') label=_('Imported by VRF'),
fetch_trigger='open'
) )
exporting_vrf_id = DynamicModelMultipleChoiceField( exporting_vrf_id = DynamicModelMultipleChoiceField(
queryset=VRF.objects.all(), queryset=VRF.objects.all(),
required=False, required=False,
label=_('Exported by VRF') label=_('Exported by VRF'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -345,7 +349,8 @@ class AggregateFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFil
rir_id = DynamicModelMultipleChoiceField( rir_id = DynamicModelMultipleChoiceField(
queryset=RIR.objects.all(), queryset=RIR.objects.all(),
required=False, required=False,
label=_('RIR') label=_('RIR'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -642,12 +647,14 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilter
queryset=VRF.objects.all(), queryset=VRF.objects.all(),
required=False, required=False,
label=_('Assigned VRF'), label=_('Assigned VRF'),
null_option='Global' null_option='Global',
fetch_trigger='open'
) )
present_in_vrf_id = DynamicModelChoiceField( present_in_vrf_id = DynamicModelChoiceField(
queryset=VRF.objects.all(), queryset=VRF.objects.all(),
required=False, required=False,
label=_('Present in VRF') label=_('Present in VRF'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=PrefixStatusChoices, choices=PrefixStatusChoices,
@ -657,12 +664,14 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilter
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -671,13 +680,15 @@ class PrefixFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilter
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
role_id = DynamicModelMultipleChoiceField( role_id = DynamicModelMultipleChoiceField(
queryset=Role.objects.all(), queryset=Role.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Role') label=_('Role'),
fetch_trigger='open'
) )
is_pool = forms.NullBooleanField( is_pool = forms.NullBooleanField(
required=False, required=False,
@ -818,7 +829,8 @@ class IPRangeFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
queryset=VRF.objects.all(), queryset=VRF.objects.all(),
required=False, required=False,
label=_('Assigned VRF'), label=_('Assigned VRF'),
null_option='Global' null_option='Global',
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=PrefixStatusChoices, choices=PrefixStatusChoices,
@ -829,7 +841,8 @@ class IPRangeFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
queryset=Role.objects.all(), queryset=Role.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Role') label=_('Role'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -1265,12 +1278,14 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFil
queryset=VRF.objects.all(), queryset=VRF.objects.all(),
required=False, required=False,
label=_('Assigned VRF'), label=_('Assigned VRF'),
null_option='Global' null_option='Global',
fetch_trigger='open'
) )
present_in_vrf_id = DynamicModelChoiceField( present_in_vrf_id = DynamicModelChoiceField(
queryset=VRF.objects.all(), queryset=VRF.objects.all(),
required=False, required=False,
label=_('Present in VRF') label=_('Present in VRF'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=IPAddressStatusChoices, choices=IPAddressStatusChoices,
@ -1439,27 +1454,32 @@ class VLANGroupFilterForm(BootstrapMixin, forms.Form):
region = DynamicModelMultipleChoiceField( region = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
sitegroup = DynamicModelMultipleChoiceField( sitegroup = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site = DynamicModelMultipleChoiceField( site = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
required=False, required=False,
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
location = DynamicModelMultipleChoiceField( location = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
required=False, required=False,
label=_('Location') label=_('Location'),
fetch_trigger='open'
) )
rack = DynamicModelMultipleChoiceField( rack = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(), queryset=Rack.objects.all(),
required=False, required=False,
label=_('Rack') label=_('Rack'),
fetch_trigger='open'
) )
@ -1652,12 +1672,14 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -1666,7 +1688,8 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
query_params={ query_params={
'region': '$region' 'region': '$region'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
group_id = DynamicModelMultipleChoiceField( group_id = DynamicModelMultipleChoiceField(
queryset=VLANGroup.objects.all(), queryset=VLANGroup.objects.all(),
@ -1675,7 +1698,8 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
query_params={ query_params={
'region': '$region' 'region': '$region'
}, },
label=_('VLAN group') label=_('VLAN group'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=VLANStatusChoices, choices=VLANStatusChoices,
@ -1686,7 +1710,8 @@ class VLANFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilterFo
queryset=Role.objects.all(), queryset=Role.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Role') label=_('Role'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ import queryString from 'query-string';
import { readableColor } from 'color2k'; import { readableColor } from 'color2k';
import SlimSelect from 'slim-select'; import SlimSelect from 'slim-select';
import { createToast } from '../bs'; import { createToast } from '../bs';
import { hasUrl, hasExclusions } from './util'; import { hasUrl, hasExclusions, isTrigger } from './util';
import { import {
isTruthy, isTruthy,
hasError, hasError,
@ -10,6 +10,7 @@ import {
getApiData, getApiData,
isApiError, isApiError,
getElements, getElements,
createElement,
findFirstAdjacent, findFirstAdjacent,
} from '../util'; } from '../util';
@ -17,6 +18,20 @@ import type { Option } from 'slim-select/dist/data';
type QueryFilter = Map<string, string | number | boolean>; type QueryFilter = Map<string, string | number | boolean>;
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. // Various one-off patterns to replace in query param keys.
const REPLACE_PATTERNS = [ const REPLACE_PATTERNS = [
// Don't query `termination_a_device=1`, but rather `device=1`. // Don't query `termination_a_device=1`, but rather `device=1`.
@ -57,6 +72,17 @@ class APISelect {
*/ */
public readonly placeholder: string; 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 `<input/>` element.
*/
private readonly allowRefresh: boolean = true;
/** /**
* Event to be dispatched when dependent fields' values change. * Event to be dispatched when dependent fields' values change.
*/ */
@ -153,6 +179,7 @@ class APISelect {
allowDeselect: true, allowDeselect: true,
deselectLabel: `<i class="mdi mdi-close-circle" style="color:currentColor;"></i>`, deselectLabel: `<i class="mdi mdi-close-circle" style="color:currentColor;"></i>`,
placeholder: this.placeholder, placeholder: this.placeholder,
searchPlaceholder: 'Filter',
onChange: () => this.handleSlimChange(), onChange: () => this.handleSlimChange(),
}); });
@ -186,20 +213,44 @@ class APISelect {
// Initialize controlling elements. // Initialize controlling elements.
this.initResetButton(); this.initResetButton();
// Add the refresh button to the search element.
this.initRefreshButton();
// Add dependency event listeners. // Add dependency event listeners.
this.addEventListeners(); 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. // Determine if this element is part of collapsible element.
const collapse = this.base.closest('.content-container .collapse'); 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 if (isTrigger(triggerAttr)) {
// collapsible element is shown. this.trigger = triggerAttr;
// See: https://getbootstrap.com/docs/5.0/components/collapse/#events } else if (collapse !== null) {
collapse.addEventListener('show.bs.collapse', () => this.loadData()); this.trigger = 'collapse';
collapse.addEventListener('hide.bs.collapse', () => this.resetOptions());
} else { } else {
// Otherwise, load the data on render. this.trigger = 'load';
Promise.all([this.loadData()]); }
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 { private initResetButton(): void {
const resetButton = findFirstAdjacent<HTMLButtonElement>(this.base, 'button[data-reset-select'); const resetButton = findFirstAdjacent<HTMLButtonElement>(
this.base,
'button[data-reset-select]',
);
if (resetButton !== null) { if (resetButton !== null) {
resetButton.addEventListener('click', () => { resetButton.addEventListener('click', () => {
this.base.value = ''; window.location.assign(window.location.origin + window.location.pathname);
if (this.base.multiple) {
this.slim.setSelected([]);
} else {
this.slim.setSelected('');
}
}); });
} }
} }
/**
* 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() { export function initApiSelect() {

View File

@ -1,3 +1,5 @@
import type { Trigger } from './api';
/** /**
* Determine if an element has the `data-url` attribute set. * 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'); const exclude = el.getAttribute('data-query-param-exclude');
return typeof exclude === 'string' && 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);
}

View File

@ -52,7 +52,7 @@
} }
* { * {
transition: $transition-100ms-ease-in-out; transition: background-color, color 0.1s ease-in-out;
} }
.mw-25 { .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) { div#advanced-search-content {
margin-right: 1rem; &.collapsing {
transition: height 0.1s ease-in-out;
}
div.card div.card-body div.col:not(:last-child) {
margin-right: 1rem;
}
} }
body { body {
@ -430,6 +435,7 @@ nav.search {
background-color: var(--nbx-body-bg); background-color: var(--nbx-body-bg);
// Don't overtake dropdowns // Don't overtake dropdowns
z-index: 999; z-index: 999;
justify-content: center;
form button.dropdown-toggle { form button.dropdown-toggle {
border-color: $input-border-color; border-color: $input-border-color;
font-weight: $input-group-addon-font-weight; font-weight: $input-group-addon-font-weight;

View File

@ -71,8 +71,8 @@ $spacing-s: $input-padding-x;
border-color: currentColor; border-color: currentColor;
} }
} }
// Don't show the depth indicator outside of the menu.
.placeholder .depth { .placeholder .depth {
// Don't show the depth indicator outside of the menu.
display: none; display: none;
} }
span.placeholder > *, span.placeholder > *,
@ -94,6 +94,11 @@ $spacing-s: $input-padding-x;
.ss-value { .ss-value {
border-radius: $badge-border-radius; border-radius: $badge-border-radius;
color: var(--nbx-select-value-color); color: var(--nbx-select-value-color);
// Don't show the depth indicator outside of the menu.
.depth {
display: none;
}
} }
} }
.ss-add { .ss-add {
@ -133,10 +138,34 @@ $spacing-s: $input-padding-x;
opacity: 0.3; 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-left-radius: $form-select-border-radius;
border-bottom-right-radius: $form-select-border-radius; border-bottom-right-radius: $form-select-border-radius;
.ss-search { .ss-search {
padding-right: $spacer * 0.5;
button {
margin-left: $spacer * 0.75;
}
input[type='search'] { input[type='search'] {
background-color: $form-select-bg; background-color: $form-select-bg;
color: $input-color; color: $input-color;

View File

@ -1,42 +1,24 @@
{% extends 'base/layout.html' %} {% extends 'base/layout.html' %}
{% load buttons %} {% load buttons %}
{% load render_table from django_tables2 %}
{% block title %}{{ title }}{% endblock %} {% block title %}{{ title }}{% endblock %}
{% block extra_controls %}{% export_button content_type %}{% endblock %} {% block extra_controls %}{% export_button content_type %}{% endblock %}
{% block content %} {% block content %}
{% if filter_form %} <div class="row mb-3">
<div class="col col-md-12 noprint"> <div class="col col-md-7 col-lg-8 col-xl-9 col-xxl-10">
{% include 'inc/advanced_search.html' %} {% include 'inc/table_controls.html' %}
</div>
{% endif %} <div class="table-responsive">
<div class="row mb-3"> {% render_table table 'inc/table.html' %}
<div class="col col-md-12">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-4 offset-md-8 d-flex noprint table-controls">
<div class="input-group input-group-sm">
<input type="text" class="form-control object-filter" placeholder="Filter" title="Filter text (regular expressions supported)" />
{% if filter_form %}
<button
type="button"
class="btn btn-sm btn-outline-dark"
data-bs-toggle="collapse"
data-bs-target="#advanced-search-content">
Advanced Search
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body">
{% include 'inc/responsive_table.html' %}
</div> </div>
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
</div> </div>
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} {% if filter_form %}
{% include 'inc/filter_list.html' %}
{% endif %}
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -5,63 +5,58 @@
{% block title %}Rack Elevations{% endblock %} {% block title %}Rack Elevations{% endblock %}
{% block controls %} {% block controls %}
<div class="container mb-2 mx-0"> <div class="container mb-2 mx-0">
<div class="d-flex flex-wrap justify-content-end"> <div class="d-flex flex-wrap justify-content-end">
<button type="button" class="btn btn-sm btn-outline-dark m-1" data-bs-toggle="collapse" data-bs-target="#advanced-search-content"> <button class="btn btn-sm btn-outline-dark toggle-images m-1" selected="selected">
Advanced Search <span class="mdi mdi mdi-checkbox-marked-circle-outline" aria-hidden="true"></span> Show Images
</button> </button>
<button class="btn btn-sm btn-outline-dark toggle-images m-1" selected="selected"> <div class="btn-group btn-group-sm m-1" role="group">
<span class="mdi mdi mdi-checkbox-marked-circle-outline" aria-hidden="true"></span> Show Images <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='front' %}" class="btn btn-outline-secondary{% if rack_face == 'front' %} active{% endif %}">Front</a>
</button> <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='rear' %}" class="btn btn-outline-secondary{% if rack_face == 'rear' %} active{% endif %}">Rear</a>
<div class="btn-group btn-group-sm m-1" role="group"> </div>
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='front' %}" class="btn btn-outline-secondary{% if rack_face == 'front' %} active{% endif %}">Front</a> <div class="btn-group btn-group-sm m-1" role="group">
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='rear' %}" class="btn btn-outline-secondary{% if rack_face == 'rear' %} active{% endif %}">Rear</a> <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request reverse=None %}" class="btn btn-outline-secondary{% if not reverse %} active{% endif %}">Normal</a>
</div> <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request reverse='true' %}" class="btn btn-outline-secondary{% if reverse %} active{% endif %}">Reversed</a>
<div class="btn-group btn-group-sm m-1" role="group"> </div>
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request reverse=None %}" class="btn btn-outline-secondary{% if not reverse %} active{% endif %}">Normal</a>
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request reverse='true' %}" class="btn btn-outline-secondary{% if reverse %} active{% endif %}">Reversed</a>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col col-md-12 noprint"> <div class="row">
{% include 'inc/advanced_search.html' %} <div class="col col-md-7 col-lg-8 col-xl-9 col-xxl-10">
</div> {% if page %}
<div class="row"> <div style="white-space: nowrap; overflow-x: scroll;">
<div class="col col-md-12"> {% for rack in page %}
{% if page %} <div style="display: inline-block; margin-right: 12px; width: 254px">
<div style="white-space: nowrap; overflow-x: scroll;"> <div style="margin-left: 30px">
{% for rack in page %} <div class="text-center">
<div style="display: inline-block; margin-right: 12px; width: 254px"> <strong><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></strong>
<div style="margin-left: 30px"> {% if rack.role %}
<div class="text-center"> <br /><span class="badge my-3" style="color: {{ rack.role.color|fgcolor }}; background-color: #{{ rack.role.color }}">{{ rack.role }}</span>
<strong><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></strong> {% endif %}
{% if rack.role %} {% if rack.facility_id %}
<br /><span class="badge my-3" style="color: {{ rack.role.color|fgcolor }}; background-color: #{{ rack.role.color }}">{{ rack.role }}</span> <br /><small class="text-muted">{{ rack.facility_id }}</small>
{% endif %} {% endif %}
{% if rack.facility_id %} </div>
<br /><small class="text-muted">{{ rack.facility_id }}</small> {% include 'dcim/inc/rack_elevation.html' with object=rack face=rack_face %}
{% endif %} <div class="clearfix"></div>
<div class="text-center">
<strong><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></strong>
{% if rack.facility_id %}
<small class="text-muted">({{ rack.facility_id }})</small>
{% endif %}
</div>
</div> </div>
{% include 'dcim/inc/rack_elevation.html' with object=rack face=rack_face %}
<div class="clearfix"></div>
<div class="text-center">
<strong><a href="{% url 'dcim:rack' pk=rack.pk %}">{{ rack.name }}</a></strong>
{% if rack.facility_id %}
<small class="text-muted">({{ rack.facility_id }})</small>
{% endif %}
</div> </div>
</div> {% endfor %}
</div> </div>
{% endfor %} <br />
</div> {% include 'inc/paginator.html' %}
<br /> {% else %}
{% include 'inc/paginator.html' %} <p>No Racks Found</p>
{% else %} {% endif %}
<p>No Racks Found</p> </div>
{% endif %} {% include 'inc/filter_list.html' %}
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -24,9 +24,6 @@
{% endblock controls %} {% endblock controls %}
{% block content %} {% block content %}
{% if filter_form %}
{% include 'inc/advanced_search.html' %}
{% endif %}
{% if table.paginator.num_pages > 1 %} {% 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" %} {% with bulk_edit_url=content_type.model_class|validated_viewname:"bulk_edit" bulk_delete_url=content_type.model_class|validated_viewname:"bulk_delete" %}
<div id="select-all-box" class="d-none card noprint"> <div id="select-all-box" class="d-none card noprint">
@ -57,12 +54,12 @@
{% endwith %} {% endwith %}
{% endif %} {% endif %}
{# Object list filter, table config #}
{% include 'inc/table_controls.html' with table_modal="ObjectTable_config" %}
{# Object table #} {# Object table #}
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-7 col-lg-8 col-xl-9 col-xxl-10">
{# 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" %} {% 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 %} {% if permissions.change or permissions.delete %}
<form method="post" class="form form-horizontal"> <form method="post" class="form form-horizontal">
@ -95,6 +92,9 @@
{% endwith %} {% endwith %}
{% include 'inc/paginator.html' with paginator=table.paginator page=table.page %} {% include 'inc/paginator.html' with paginator=table.paginator page=table.page %}
</div> </div>
{% if filter_form %}
{% include 'inc/filter_list.html' %}
{% endif %}
</div> </div>
{% table_config_form table table_name="ObjectTable" %} {% table_config_form table table_name="ObjectTable" %}
{% endblock content %} {% endblock content %}

View File

@ -0,0 +1,62 @@
{% load form_helpers %}
{% load helpers %}
<div class="col col-md-5 col-lg-4 col-xl-3 col-xxl-2 noprint">
<form action="." method="get">
<div class="card small">
<h5 class="card-header">
Field Filters
</h5>
<div class="card-body overflow-visible d-flex flex-wrap justify-content-between py-3">
{% for field in filter_form.hidden_fields %}
{{ field }}
{% endfor %}
{% if filter_form.field_groups %}
{% for group in filter_form.field_groups %}
<div class="col col-12">
{% for name in group %}
{% with field=filter_form|get_item:name %}
{% if field|widget_type == 'checkboxinput' %}
<div class="form-check mb-3">
<label class="form-check-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
</div>
{% else %}
<div class="mb-3 mx-3">
<label class="form-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
</div>
{% endif %}
{% endwith %}
{% endfor %}
</div>
{% endfor %}
{% else %}
{% for field in filter_form.visible_fields %}
<div class="col">
{% if field|widget_type == 'checkboxinput' %}
<div class="form-check mb-3">
<label class="form-check-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
</div>
{% else %}
<div class="mb-3">
<label class="form-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
</div>
<div class="card-footer text-end noprint border-0">
<button type="button" class="btn btn-sm btn-outline-danger m-1" data-reset-select>
<i class="mdi mdi-backspace"></i> Reset
</button>
<button type="submit" class="btn btn-sm btn-primary m-1">
<i class="mdi mdi-filter-variant"></i> Filter
</button>
</div>
</div>
</form>
</div>

View File

@ -1,6 +1,6 @@
<div class="row mb-3 justify-content-between"> <div class="row mb-3 justify-content-between">
<div class="col col-md-2 mb-0 d-flex noprint table-controls"> <div class="col col-md-2 mb-0 d-flex noprint table-controls">
{% if request.user.is_authenticated %} {% if request.user.is_authenticated and table_modal %}
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<button <button
type="button" type="button"
@ -22,16 +22,6 @@
placeholder="Filter" placeholder="Filter"
title="Filter text (regular expressions supported)" title="Filter text (regular expressions supported)"
/> />
{% if filter_form %}
<button
type="button"
class="btn btn-sm btn-outline-dark"
data-bs-toggle="collapse"
data-bs-target="#advanced-search-content"
>
Advanced Search
</button>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -67,7 +67,8 @@ class TenantGroupFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=TenantGroup.objects.all(), queryset=TenantGroup.objects.all(),
required=False, required=False,
label=_('Parent group') label=_('Parent group'),
fetch_trigger='open'
) )
@ -137,7 +138,8 @@ class TenantFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
queryset=TenantGroup.objects.all(), queryset=TenantGroup.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Group') label=_('Group'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -169,7 +171,8 @@ class TenancyFilterForm(forms.Form):
queryset=TenantGroup.objects.all(), queryset=TenantGroup.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Tenant group') label=_('Tenant group'),
fetch_trigger='open'
) )
tenant_id = DynamicModelMultipleChoiceField( tenant_id = DynamicModelMultipleChoiceField(
queryset=Tenant.objects.all(), queryset=Tenant.objects.all(),
@ -178,5 +181,6 @@ class TenancyFilterForm(forms.Form):
query_params={ query_params={
'group_id': '$tenant_group_id' 'group_id': '$tenant_group_id'
}, },
label=_('Tenant') label=_('Tenant'),
fetch_trigger='open'
) )

View File

@ -363,16 +363,19 @@ class DynamicModelChoiceMixin:
:param null_option: The string used to represent a null selection (if any) :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 :param disabled_indicator: The name of the field which, if populated, will disable selection of the
choice (optional) 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 filter = django_filters.ModelChoiceFilter
widget = widgets.APISelect 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): **kwargs):
self.query_params = query_params or {} self.query_params = query_params or {}
self.initial_params = initial_params or {} self.initial_params = initial_params or {}
self.null_option = null_option self.null_option = null_option
self.disabled_indicator = disabled_indicator 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 # to_field_name is set by ModelChoiceField.__init__(), but we need to set it early for reference
# by widget_attrs() # by widget_attrs()
@ -394,6 +397,10 @@ class DynamicModelChoiceMixin:
# Set the disabled indicator, if any # Set the disabled indicator, if any
if self.disabled_indicator is not None: if self.disabled_indicator is not None:
attrs['disabled-indicator'] = self.disabled_indicator 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 # Attach any static query parameters
for key, value in self.query_params.items(): for key, value in self.query_params.items():

View File

@ -236,12 +236,14 @@ class ClusterFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
type_id = DynamicModelMultipleChoiceField( type_id = DynamicModelMultipleChoiceField(
queryset=ClusterType.objects.all(), queryset=ClusterType.objects.all(),
required=False, required=False,
label=_('Type') label=_('Type'),
fetch_trigger='open'
) )
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -250,13 +252,15 @@ class ClusterFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldModelFilte
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
group_id = DynamicModelMultipleChoiceField( group_id = DynamicModelMultipleChoiceField(
queryset=ClusterGroup.objects.all(), queryset=ClusterGroup.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Group') label=_('Group'),
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -547,28 +551,33 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod
queryset=ClusterGroup.objects.all(), queryset=ClusterGroup.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Cluster group') label=_('Cluster group'),
fetch_trigger='open'
) )
cluster_type_id = DynamicModelMultipleChoiceField( cluster_type_id = DynamicModelMultipleChoiceField(
queryset=ClusterType.objects.all(), queryset=ClusterType.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Cluster type') label=_('Cluster type'),
fetch_trigger='open'
) )
cluster_id = DynamicModelMultipleChoiceField( cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(), queryset=Cluster.objects.all(),
required=False, required=False,
label=_('Cluster') label=_('Cluster'),
fetch_trigger='open'
) )
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Region') label=_('Region'),
fetch_trigger='open'
) )
site_group_id = DynamicModelMultipleChoiceField( site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group') label=_('Site group'),
fetch_trigger='open'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
@ -578,7 +587,8 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod
'region_id': '$region_id', 'region_id': '$region_id',
'group_id': '$site_group_id', 'group_id': '$site_group_id',
}, },
label=_('Site') label=_('Site'),
fetch_trigger='open'
) )
role_id = DynamicModelMultipleChoiceField( role_id = DynamicModelMultipleChoiceField(
queryset=DeviceRole.objects.all(), queryset=DeviceRole.objects.all(),
@ -587,7 +597,8 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod
query_params={ query_params={
'vm_role': "True" 'vm_role': "True"
}, },
label=_('Role') label=_('Role'),
fetch_trigger='open'
) )
status = forms.MultipleChoiceField( status = forms.MultipleChoiceField(
choices=VirtualMachineStatusChoices, choices=VirtualMachineStatusChoices,
@ -598,7 +609,8 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldMod
queryset=Platform.objects.all(), queryset=Platform.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Platform') label=_('Platform'),
fetch_trigger='open'
) )
mac_address = forms.CharField( mac_address = forms.CharField(
required=False, required=False,
@ -850,7 +862,8 @@ class VMInterfaceFilterForm(BootstrapMixin, forms.Form):
cluster_id = DynamicModelMultipleChoiceField( cluster_id = DynamicModelMultipleChoiceField(
queryset=Cluster.objects.all(), queryset=Cluster.objects.all(),
required=False, required=False,
label=_('Cluster') label=_('Cluster'),
fetch_trigger='open'
) )
virtual_machine_id = DynamicModelMultipleChoiceField( virtual_machine_id = DynamicModelMultipleChoiceField(
queryset=VirtualMachine.objects.all(), queryset=VirtualMachine.objects.all(),
@ -858,7 +871,8 @@ class VMInterfaceFilterForm(BootstrapMixin, forms.Form):
query_params={ query_params={
'cluster_id': '$cluster_id' 'cluster_id': '$cluster_id'
}, },
label=_('Virtual machine') label=_('Virtual machine'),
fetch_trigger='open'
) )
enabled = forms.NullBooleanField( enabled = forms.NullBooleanField(
required=False, required=False,