Merge v3.1.3

This commit is contained in:
jeremystretch 2021-12-29 12:41:56 -05:00
commit 3bb485d0b8
37 changed files with 361 additions and 537 deletions

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v3.1.2 placeholder: v3.1.3
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v3.1.2 placeholder: v3.1.3
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -1,16 +1,23 @@
# NetBox v3.1 # NetBox v3.1
## v3.1.3 (FUTURE) ## v3.1.4 (FUTURE)
---
## v3.1.3 (2021-12-29)
### Enhancements ### Enhancements
* [#6782](https://github.com/netbox-community/netbox/issues/6782) - Enable the inclusion of custom links in tables * [#6782](https://github.com/netbox-community/netbox/issues/6782) - Enable the inclusion of custom links in tables
* [#7600](https://github.com/netbox-community/netbox/issues/7600) - Include count of available IPs on prefix view
* [#8034](https://github.com/netbox-community/netbox/issues/8034) - Enable specifying custom field validators during CSV import
* [#8100](https://github.com/netbox-community/netbox/issues/8100) - Add "other" choice for FHRP group protocol * [#8100](https://github.com/netbox-community/netbox/issues/8100) - Add "other" choice for FHRP group protocol
* [#8175](https://github.com/netbox-community/netbox/issues/8175) - Display parent object when attaching an image * [#8175](https://github.com/netbox-community/netbox/issues/8175) - Display parent object when attaching an image
### Bug Fixes ### Bug Fixes
* [#7246](https://github.com/netbox-community/netbox/issues/7246) - Don't attempt to URL-decode NAPALM response payloads * [#7246](https://github.com/netbox-community/netbox/issues/7246) - Don't attempt to URL-decode NAPALM response payloads
* [#7290](https://github.com/netbox-community/netbox/issues/7290) - Defer loading API-backed form fields
* [#7887](https://github.com/netbox-community/netbox/issues/7887) - Forward `HTTP_X_FORWARDED_FOR` to custom scripts * [#7887](https://github.com/netbox-community/netbox/issues/7887) - Forward `HTTP_X_FORWARDED_FOR` to custom scripts
* [#7962](https://github.com/netbox-community/netbox/issues/7962) - Fix user menu under report/script result view * [#7962](https://github.com/netbox-community/netbox/issues/7962) - Fix user menu under report/script result view
* [#7972](https://github.com/netbox-community/netbox/issues/7972) - Standardize name of `RemoteUserBackend` logger * [#7972](https://github.com/netbox-community/netbox/issues/7972) - Standardize name of `RemoteUserBackend` logger

View File

@ -26,14 +26,12 @@ class ProviderFilterForm(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(),
@ -42,8 +40,7 @@ class ProviderFilterForm(CustomFieldModelFilterForm):
'region_id': '$region_id', 'region_id': '$region_id',
'site_group_id': '$site_group_id', 'site_group_id': '$site_group_id',
}, },
label=_('Site'), label=_('Site')
fetch_trigger='open'
) )
asn = forms.IntegerField( asn = forms.IntegerField(
required=False, required=False,
@ -61,8 +58,7 @@ class ProviderNetworkFilterForm(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'
) )
service_id = forms.CharField( service_id = forms.CharField(
max_length=100, max_length=100,
@ -88,14 +84,12 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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(),
@ -103,8 +97,7 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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,
@ -114,14 +107,12 @@ class CircuitFilterForm(TenancyFilterForm, 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(),
@ -130,8 +121,7 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
'region_id': '$region_id', 'region_id': '$region_id',
'site_group_id': '$site_group_id', 'site_group_id': '$site_group_id',
}, },
label=_('Site'), label=_('Site')
fetch_trigger='open'
) )
commit_rate = forms.IntegerField( commit_rate = forms.IntegerField(
required=False, required=False,

View File

@ -62,14 +62,12 @@ class DeviceComponentFilterForm(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(),
@ -78,8 +76,7 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm):
'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'
) )
location_id = DynamicModelMultipleChoiceField( location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -87,14 +84,12 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm):
query_params={ query_params={
'site_id': '$site_id', 'site_id': '$site_id',
}, },
label=_('Location'), label=_('Location')
fetch_trigger='open'
) )
virtual_chassis_id = DynamicModelMultipleChoiceField( virtual_chassis_id = DynamicModelMultipleChoiceField(
queryset=VirtualChassis.objects.all(), queryset=VirtualChassis.objects.all(),
required=False, required=False,
label=_('Virtual Chassis'), label=_('Virtual Chassis')
fetch_trigger='open'
) )
device_id = DynamicModelMultipleChoiceField( device_id = DynamicModelMultipleChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),
@ -104,8 +99,7 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm):
'location_id': '$location_id', 'location_id': '$location_id',
'virtual_chassis_id': '$virtual_chassis_id' 'virtual_chassis_id': '$virtual_chassis_id'
}, },
label=_('Device'), label=_('Device')
fetch_trigger='open'
) )
@ -114,8 +108,7 @@ class RegionFilterForm(CustomFieldModelFilterForm):
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), queryset=Region.objects.all(),
required=False, required=False,
label=_('Parent region'), label=_('Parent region')
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -125,8 +118,7 @@ class SiteGroupFilterForm(CustomFieldModelFilterForm):
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Parent group'), label=_('Parent group')
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -147,20 +139,17 @@ class SiteFilterForm(TenancyFilterForm, 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'
) )
group_id = DynamicModelMultipleChoiceField( group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(), queryset=SiteGroup.objects.all(),
required=False, required=False,
label=_('Site group'), label=_('Site group')
fetch_trigger='open'
) )
asn_id = DynamicModelMultipleChoiceField( asn_id = DynamicModelMultipleChoiceField(
queryset=ASN.objects.all(), queryset=ASN.objects.all(),
required=False, required=False,
label=_('ASNs'), label=_('ASNs')
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -175,14 +164,12 @@ class LocationFilterForm(TenancyFilterForm, 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(),
@ -191,8 +178,7 @@ class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
'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'
) )
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -201,8 +187,7 @@ class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
'region_id': '$region_id', 'region_id': '$region_id',
'site_id': '$site_id', 'site_id': '$site_id',
}, },
label=_('Parent'), label=_('Parent')
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -224,8 +209,7 @@ class RackFilterForm(TenancyFilterForm, 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(),
@ -233,8 +217,7 @@ class RackFilterForm(TenancyFilterForm, 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(),
@ -243,8 +226,7 @@ class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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,
@ -265,8 +247,7 @@ class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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'
) )
serial = forms.CharField( serial = forms.CharField(
required=False required=False
@ -285,8 +266,7 @@ 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'
) )
@ -301,8 +281,7 @@ class RackReservationFilterForm(TenancyFilterForm, 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(),
@ -310,15 +289,13 @@ class RackReservationFilterForm(TenancyFilterForm, 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.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(),
@ -326,8 +303,7 @@ class RackReservationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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)
@ -347,8 +323,7 @@ class DeviceTypeFilterForm(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'
) )
part_number = forms.CharField( part_number = forms.CharField(
required=False required=False
@ -479,8 +454,7 @@ class PlatformFilterForm(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'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -501,14 +475,12 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
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(),
@ -517,8 +489,7 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
'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'
) )
location_id = DynamicModelMultipleChoiceField( location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -527,8 +498,7 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
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(),
@ -538,20 +508,17 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
'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(),
@ -559,15 +526,13 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
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,
@ -689,14 +654,12 @@ class VirtualChassisFilterForm(TenancyFilterForm, 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(),
@ -705,8 +668,7 @@ class VirtualChassisFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
'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'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -722,8 +684,7 @@ class CableFilterForm(TenancyFilterForm, 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(),
@ -731,8 +692,7 @@ class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
query_params={ query_params={
'region_id': '$region_id' 'region_id': '$region_id'
}, },
label=_('Site'), label=_('Site')
fetch_trigger='open'
) )
rack_id = DynamicModelMultipleChoiceField( rack_id = DynamicModelMultipleChoiceField(
queryset=Rack.objects.all(), queryset=Rack.objects.all(),
@ -741,8 +701,7 @@ class CableFilterForm(TenancyFilterForm, 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),
@ -765,8 +724,7 @@ class CableFilterForm(TenancyFilterForm, 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)
@ -780,14 +738,12 @@ class PowerPanelFilterForm(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(),
@ -796,8 +752,7 @@ class PowerPanelFilterForm(CustomFieldModelFilterForm):
'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'
) )
location_id = DynamicModelMultipleChoiceField( location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(), queryset=Location.objects.all(),
@ -806,8 +761,7 @@ class PowerPanelFilterForm(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)
@ -823,14 +777,12 @@ class PowerFeedFilterForm(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(),
@ -838,8 +790,7 @@ class PowerFeedFilterForm(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(),
@ -848,8 +799,7 @@ class PowerFeedFilterForm(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(),
@ -858,8 +808,7 @@ class PowerFeedFilterForm(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,
@ -1109,8 +1058,7 @@ 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
@ -1144,8 +1092,7 @@ class ConsoleConnectionFilterForm(FilterForm):
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(),
@ -1153,8 +1100,7 @@ class ConsoleConnectionFilterForm(FilterForm):
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(),
@ -1162,8 +1108,7 @@ class ConsoleConnectionFilterForm(FilterForm):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Device'), label=_('Device')
fetch_trigger='open'
) )
@ -1171,8 +1116,7 @@ class PowerConnectionFilterForm(FilterForm):
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(),
@ -1180,8 +1124,7 @@ class PowerConnectionFilterForm(FilterForm):
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(),
@ -1189,8 +1132,7 @@ class PowerConnectionFilterForm(FilterForm):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Device'), label=_('Device')
fetch_trigger='open'
) )
@ -1198,8 +1140,7 @@ class InterfaceConnectionFilterForm(FilterForm):
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(),
@ -1207,8 +1148,7 @@ class InterfaceConnectionFilterForm(FilterForm):
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(),
@ -1216,6 +1156,5 @@ class InterfaceConnectionFilterForm(FilterForm):
query_params={ query_params={
'site_id': '$site_id' 'site_id': '$site_id'
}, },
label=_('Device'), label=_('Device')
fetch_trigger='open'
) )

View File

@ -300,16 +300,14 @@ class RackReservationForm(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(),
@ -317,24 +315,21 @@ class RackReservationForm(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(),
@ -348,8 +343,7 @@ class RackReservationForm(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:

View File

@ -3,9 +3,10 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.forms import SimpleArrayField from django.contrib.postgres.forms import SimpleArrayField
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from extras.choices import CustomFieldTypeChoices
from extras.models import * from extras.models import *
from extras.utils import FeatureQuery from extras.utils import FeatureQuery
from utilities.forms import CSVContentTypeField, CSVModelForm, CSVMultipleContentTypeField, SlugField from utilities.forms import CSVChoiceField, CSVContentTypeField, CSVModelForm, CSVMultipleContentTypeField, SlugField
__all__ = ( __all__ = (
'CustomFieldCSVForm', 'CustomFieldCSVForm',
@ -22,6 +23,10 @@ class CustomFieldCSVForm(CSVModelForm):
limit_choices_to=FeatureQuery('custom_fields'), limit_choices_to=FeatureQuery('custom_fields'),
help_text="One or more assigned object types" help_text="One or more assigned object types"
) )
type = CSVChoiceField(
choices=CustomFieldTypeChoices,
help_text='Field data type (e.g. text, integer, etc.)'
)
choices = SimpleArrayField( choices = SimpleArrayField(
base_field=forms.CharField(), base_field=forms.CharField(),
required=False, required=False,
@ -32,7 +37,7 @@ class CustomFieldCSVForm(CSVModelForm):
model = CustomField model = CustomField
fields = ( fields = (
'name', 'label', 'type', 'content_types', 'required', 'description', 'weight', 'filter_logic', 'default', 'name', 'label', 'type', 'content_types', 'required', 'description', 'weight', 'filter_logic', 'default',
'choices', 'weight', 'choices', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex',
) )

View File

@ -164,38 +164,32 @@ class ConfigContextFilterForm(FilterForm):
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_type_id = DynamicModelMultipleChoiceField( cluster_type_id = DynamicModelMultipleChoiceField(
queryset=ClusterType.objects.all(), queryset=ClusterType.objects.all(),
@ -206,33 +200,28 @@ class ConfigContextFilterForm(FilterForm):
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'
) )
@ -269,8 +258,7 @@ class JournalEntryFilterForm(FilterForm):
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(),
@ -278,8 +266,7 @@ class JournalEntryFilterForm(FilterForm):
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),
@ -316,8 +303,7 @@ class ObjectChangeFilterForm(FilterForm):
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(),
@ -325,6 +311,5 @@ class ObjectChangeFilterForm(FilterForm):
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

@ -39,10 +39,10 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase):
} }
cls.csv_data = ( cls.csv_data = (
'name,label,type,content_types,weight,filter_logic,choices', 'name,label,type,content_types,weight,filter_logic,choices,validation_minimum,validation_maximum,validation_regex',
'field4,Field 4,text,dcim.site,100,exact,', 'field4,Field 4,text,dcim.site,100,exact,,,,[a-z]{3}',
'field5,Field 5,integer,dcim.site,100,exact,', 'field5,Field 5,integer,dcim.site,100,exact,,1,100,',
'field6,Field 6,select,dcim.site,100,exact,"A,B,C"', 'field6,Field 6,select,dcim.site,100,exact,"A,B,C",,,',
) )
cls.bulk_edit_data = { cls.bulk_edit_data = {

View File

@ -48,14 +48,12 @@ class VRFFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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)
@ -70,14 +68,12 @@ class RouteTargetFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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)
@ -110,8 +106,7 @@ class AggregateFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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)
@ -127,14 +122,12 @@ class ASNFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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'
) )
site_id = DynamicModelMultipleChoiceField( site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(), queryset=Site.objects.all(),
required=False, required=False,
label=_('Site'), label=_('Site')
fetch_trigger='open'
) )
@ -180,14 +173,12 @@ class PrefixFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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,
@ -197,14 +188,12 @@ class PrefixFilterForm(TenancyFilterForm, 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(),
@ -213,15 +202,13 @@ class PrefixFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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,
@ -257,8 +244,7 @@ class IPRangeFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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,
@ -269,8 +255,7 @@ class IPRangeFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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)
@ -308,14 +293,12 @@ class IPAddressFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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,
@ -377,32 +360,27 @@ class VLANGroupFilterForm(CustomFieldModelFilterForm):
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'
) )
min_vid = forms.IntegerField( min_vid = forms.IntegerField(
min_value=VLAN_VID_MIN, min_value=VLAN_VID_MIN,
@ -426,14 +404,12 @@ class VLANFilterForm(TenancyFilterForm, 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(),
@ -442,8 +418,7 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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(),
@ -452,8 +427,7 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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,
@ -464,8 +438,7 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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'
) )
vid = forms.IntegerField( vid = forms.IntegerField(
required=False, required=False,

View File

@ -418,7 +418,7 @@ class PrefixView(generic.ObjectView):
).filter( ).filter(
prefix__net_contains=str(instance.prefix) prefix__net_contains=str(instance.prefix)
).prefetch_related( ).prefetch_related(
'site', 'role' 'site', 'role', 'tenant'
) )
parent_prefix_table = tables.PrefixTable( parent_prefix_table = tables.PrefixTable(
list(parent_prefixes), list(parent_prefixes),

Binary file not shown.

View File

@ -251,7 +251,7 @@ export class APISelect {
} else if (collapse !== null) { } else if (collapse !== null) {
this.trigger = 'collapse'; this.trigger = 'collapse';
} else { } else {
this.trigger = 'load'; this.trigger = 'open';
} }
switch (this.trigger) { switch (this.trigger) {

View File

@ -20,7 +20,7 @@
</div> </div>
{# Top bar #} {# Top bar #}
<nav class="navbar navbar-light sticky-top flex-md-nowrap p-1 mb-3 search container-fluid border-bottom bg-light bg-gradient noprint"> <nav class="navbar navbar-light sticky-top flex-md-nowrap p-1 mb-3 search container-fluid border-bottom noprint">
{# Mobile Navigation #} {# Mobile Navigation #}
<div class="nav-mobile"> <div class="nav-mobile">

View File

@ -40,13 +40,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">VM Role</th> <th scope="row">VM Role</th>
<td> <td>{% checkmark object.vm_role %}</td>
{% if object.vm_role %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Devices</th> <th scope="row">Devices</th>

View File

@ -33,13 +33,7 @@
</tr> </tr>
<tr> <tr>
<td>Full Depth</td> <td>Full Depth</td>
<td> <td>{% checkmark object.is_full_depth %}</td>
{% if object.is_full_depth %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<td>Parent/Child</td> <td>Parent/Child</td>

View File

@ -48,23 +48,11 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Enabled</th> <th scope="row">Enabled</th>
<td> <td>{% checkmark object.enabled %}</td>
{% if object.enabled %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Management Only</th> <th scope="row">Management Only</th>
<td> <td>{% checkmark object.mgmt_only %}</td>
{% if object.mgmt_only %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Parent</th> <th scope="row">Parent</th>

View File

@ -65,7 +65,7 @@
</td> </td>
<td> <td>
{% if object.master == vc_member %} {% if object.master == vc_member %}
<i class="mdi mdi-check-bold text-success"></i> {% checkmark True %}
{% endif %} {% endif %}
</td> </td>
<td> <td>

View File

@ -29,13 +29,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Required</th> <th scope="row">Required</th>
<td> <td>{% checkmark object.required %}</td>
{% if object.required %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Weight</th> <th scope="row">Weight</th>

View File

@ -33,13 +33,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">New Window</th> <th scope="row">New Window</th>
<td> <td>{% checkmark object.new_window %}</td>
{% if object.new_window %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
</table> </table>
</div> </div>

View File

@ -40,13 +40,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Attachment</th> <th scope="row">Attachment</th>
<td> <td>{% checkmark object.as_attachment %}</td>
{% if object.as_attachment %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
</table> </table>
</div> </div>

View File

@ -17,13 +17,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Enabled</th> <th scope="row">Enabled</th>
<td> <td>{% checkmark object.enabled %}</td>
{% if object.enabled %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
</table> </table>
</div> </div>
@ -36,33 +30,15 @@
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
<tr> <tr>
<th scope="row">Create</th> <th scope="row">Create</th>
<td> <td>{% checkmark object.type_create %}</td>
{% if object.type_create %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Update</th> <th scope="row">Update</th>
<td> <td>{% checkmark object.type_update %}</td>
{% if object.type_update %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Delete</th> <th scope="row">Delete</th>
<td> <td>{% checkmark object.type_delete %}</td>
{% if object.type_delete %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
</table> </table>
</div> </div>
@ -100,13 +76,7 @@
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
<tr> <tr>
<th scope="row">SSL Verification</th> <th scope="row">SSL Verification</th>
<td> <td>{% checkmark object.ssl_verification %}</td>
{% if object.ssl_verification %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">CA File Path</th> <th scope="row">CA File Path</th>

View File

@ -66,7 +66,7 @@
</td> </td>
<td> <td>
{% if field.required %} {% if field.required %}
<i class="mdi mdi-check-bold text-success" title="Required"></i> {% checkmark True true="Required" %}
{% else %} {% else %}
<span class="text-muted">&mdash;</span> <span class="text-muted">&mdash;</span>
{% endif %} {% endif %}

View File

@ -15,9 +15,9 @@
{% if field.type == 'longtext' and value %} {% if field.type == 'longtext' and value %}
{{ value|render_markdown }} {{ value|render_markdown }}
{% elif field.type == 'boolean' and value == True %} {% elif field.type == 'boolean' and value == True %}
<i class="mdi mdi-check-bold text-success" title="True"></i> {% checkmark value true="True" %}
{% elif field.type == 'boolean' and value == False %} {% elif field.type == 'boolean' and value == False %}
<i class="mdi mdi-close-thick text-danger" title="False"></i> {% checkmark value false="False" %}
{% elif field.type == 'url' and value %} {% elif field.type == 'url' and value %}
<a href="{{ value }}">{{ value|truncatechars:70 }}</a> <a href="{{ value }}">{{ value|truncatechars:70 }}</a>
{% elif field.type == 'json' and value %} {% elif field.type == 'json' and value %}

View File

@ -4,127 +4,154 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-6"> <div class="col col-md-6">
<div class="card"> <div class="card">
<h5 class="card-header"> <h5 class="card-header">Prefix</h5>
Prefix <div class="card-body">
</h5> <table class="table table-hover attr-table">
<div class="card-body"> <tr>
<table class="table table-hover attr-table"> <th scope="row">Family</th>
<tr> <td>IPv{{ object.family }}</td>
<th scope="row">Family</th> </tr>
<td>IPv{{ object.family }}</td> <tr>
</tr> <th scope="row">VRF</th>
<tr> <td>
<th scope="row">VRF</th> {% if object.vrf %}
<td> <a href="{% url 'ipam:vrf' pk=object.vrf.pk %}">{{ object.vrf }}</a> ({{ object.vrf.rd }})
{% if object.vrf %} {% else %}
<a href="{% url 'ipam:vrf' pk=object.vrf.pk %}">{{ object.vrf }}</a> ({{ object.vrf.rd }}) <span>Global</span>
{% else %} {% endif %}
<span>Global</span> </td>
{% endif %} </tr>
</td> <tr>
</tr> <th scope="row">Tenant</th>
<tr> <td>
<th scope="row">Tenant</th> {% if object.tenant %}
<td> {% if object.tenant.group %}
{% if object.tenant %} <a href="{{ object.tenant.group.get_absolute_url }}">{{ object.tenant.group }}</a> /
{% if object.tenant.group %} {% endif %}
<a href="{{ object.tenant.group.get_absolute_url }}">{{ object.tenant.group }}</a> / <a href="{{ object.tenant.get_absolute_url }}">{{ object.tenant }}</a>
{% endif %} {% else %}
<a href="{{ object.tenant.get_absolute_url }}">{{ object.tenant }}</a> <span class="text-muted">None</span>
{% else %} {% endif %}
<span class="text-muted">None</span> </td>
{% endif %} </tr>
</td> <tr>
</tr> <th scope="row">Aggregate</th>
<tr> <td>
<th scope="row">Aggregate</th> {% if aggregate %}
<td> <a href="{% url 'ipam:aggregate' pk=aggregate.pk %}">{{ aggregate.prefix }}</a> ({{ aggregate.rir }})
{% if aggregate %} {% else %}
<a href="{% url 'ipam:aggregate' pk=aggregate.pk %}">{{ aggregate.prefix }}</a> ({{ aggregate.rir }}) <span class="text-warning">None</span>
{% else %} {% endif %}
<span class="text-warning">None</span> </td>
{% endif %} </tr>
</td> <tr>
</tr> <th scope="row">Site</th>
<tr> <td>
<th scope="row">Site</th> {% if object.site %}
<td> {% if object.site.region %}
{% if object.site %} <a href="{{ object.site.region.get_absolute_url }}">{{ object.site.region }}</a> /
{% if object.site.region %} {% endif %}
<a href="{{ object.site.region.get_absolute_url }}">{{ object.site.region }}</a> / <a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a>
{% endif %} {% else %}
<a href="{{ object.site.get_absolute_url }}">{{ object.site }}</a> <span class="text-muted">None</span>
{% else %} {% endif %}
<span class="text-muted">None</span> </td>
{% endif %} </tr>
</td> <tr>
</tr> <th scope="row">VLAN</th>
<tr> <td>
<th scope="row">VLAN</th> {% if object.vlan %}
<td> {% if object.vlan.group %}
{% if object.vlan %} <a href="{{ object.vlan.group.get_absolute_url }}">{{ object.vlan.group }}</a> /
{% if object.vlan.group %} {% endif %}
<a href="{{ object.vlan.group.get_absolute_url }}">{{ object.vlan.group }}</a> / <a href="{% url 'ipam:vlan' pk=object.vlan.pk %}">{{ object.vlan }}</a>
{% endif %} {% else %}
<a href="{% url 'ipam:vlan' pk=object.vlan.pk %}">{{ object.vlan }}</a> <span class="text-muted">None</span>
{% else %} {% endif %}
<span class="text-muted">None</span> </td>
{% endif %} </tr>
</td> <tr>
</tr> <th scope="row">Status</th>
<tr> <td>
<th scope="row">Status</th> <span class="badge bg-{{ object.get_status_class }}">{{ object.get_status_display }}</span>
<td> </td>
<span class="badge bg-{{ object.get_status_class }}">{{ object.get_status_display }}</span> </tr>
</td> <tr>
</tr> <th scope="row">Role</th>
<tr> <td>
<th scope="row">Role</th> {% if object.role %}
<td> <a href="{{ object.role.get_absolute_url }}">{{ object.role }}</a>
{% if object.role %} {% else %}
<a href="{{ object.role.get_absolute_url }}">{{ object.role }}</a> <span class="text-muted">None</span>
{% else %} {% endif %}
<span class="text-muted">None</span> </td>
{% endif %} </tr>
</td> <tr>
</tr> <th scope="row">Description</th>
<tr> <td>{{ object.description|placeholder }}</td>
<th scope="row">Description</th> </tr>
<td>{{ object.description|placeholder }}</td> <tr>
</tr> <th scope="row">Is a pool</th>
<tr> <td>{% checkmark object.is_pool %}</td>
<th scope="row">Is a pool</th> </tr>
<td> </table>
{% if object.is_pool %} </div>
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">Utilization</th>
<td>
{% if object.mark_utilized %}
{% utilization_graph 100 warning_threshold=0 danger_threshold=0 %}
<small>(Marked fully utilized)</small>
{% else %}
{% utilization_graph object.get_utilization %}
{% endif %}
</td>
</tr>
</table>
</div>
</div>
{% plugin_left_page object %}
</div> </div>
<div class="col col-md-6"> {% plugin_left_page object %}
{% include 'inc/panels/custom_fields.html' %} </div>
{% include 'inc/panels/tags.html' %} <div class="col col-md-6">
{% plugin_right_page object %} <div class="card">
<h5 class="card-header">Addressing</h5>
<div class="card-body">
<table class="table table-hover attr-table">
<tr>
<th scope="row">Utilization</th>
<td>
{% if object.mark_utilized %}
{% utilization_graph 100 warning_threshold=0 danger_threshold=0 %}
<small>(Marked fully utilized)</small>
{% else %}
{% utilization_graph object.get_utilization %}
{% endif %}
</td>
</tr>
{% with child_ip_count=object.get_child_ips.count %}
<tr>
<th scope="row">Child IPs</th>
<td>
<a href="{% url 'ipam:prefix_ipaddresses' pk=object.pk %}">{{ child_ip_count }}</a>
</td>
</tr>
<tr>
<th scope="row">Available IPs</th>
<td>{{ object.get_available_ips|length }}</td>
</tr>
{% endwith %}
<tr>
<td>First available IP</td>
<td>
{% with first_available_ip=object.get_first_available_ip %}
{% if first_available_ip %}
{% if perms.ipam.add_ipaddress %}
<a href="{% url 'ipam:ipaddress_add' %}?address={{ first_available_ip }}">{{ first_available_ip }}</a>
{% else %}
{{ first_available_ip }}
{% endif %}
{% else %}
<span class="text-muted">None</span>
{% endif %}
{% endwith %}
</td>
</tr>
</table>
</div>
</div> </div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% plugin_right_page object %}
</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-12">

View File

@ -30,13 +30,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Private</th> <th scope="row">Private</th>
<td> <td>{% checkmark object.is_private %}</td>
{% if object.is_private %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Aggregates</th> <th scope="row">Aggregates</th>

View File

@ -30,13 +30,7 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Unique IP Space</th> <th scope="row">Unique IP Space</th>
<td> <td>{% checkmark object.enforce_unique %}</td>
{% if object.enforce_unique %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Description</th> <th scope="row">Description</th>

View File

@ -5,7 +5,7 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-12"> <div class="col col-md-10 offset-md-1">
{% for token in tokens %} {% for token in tokens %}
<div class="card{% if token.is_expired %} bg-danger{% endif %}"> <div class="card{% if token.is_expired %} bg-danger{% endif %}">
<div class="card-header"> <div class="card-header">
@ -49,7 +49,8 @@
</div> </div>
</div> </div>
{% empty %} {% empty %}
<p>You do not have any API tokens.</p> <h6><i class="mdi mdi-information"></i> You do not have any API tokens.</h6>
<p>Tokens are used to authenticate REST and GraphQL API requests.</p>
{% endfor %} {% endfor %}
<div class="text-end"> <div class="text-end">
<a href="{% url 'user:token_add' %}" class="btn btn-sm btn-primary my-3"> <a href="{% url 'user:token_add' %}" class="btn btn-sm btn-primary my-3">

View File

@ -35,23 +35,11 @@
</tr> </tr>
<tr> <tr>
<th scope="row">Superuser</th> <th scope="row">Superuser</th>
<td> <td>{% checkmark request.user.is_superuser %}</td>
{% if request.user.is_superuser %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Admin Access</th> <th scope="row">Admin Access</th>
<td> <td>{% checkmark request.user.is_staff %}</td>
{% if request.user.is_staff %}
<i class="mdi mdi-check-bold text-success" title="Yes"></i>
{% else %}
<i class="mdi mdi-close-thick text-danger" title="No"></i>
{% endif %}
</td>
</tr> </tr>
</table> </table>
</div> </div>

View File

@ -22,8 +22,7 @@ class TenantGroupFilterForm(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'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -38,8 +37,7 @@ class TenantFilterForm(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)
@ -53,8 +51,7 @@ class ContactGroupFilterForm(CustomFieldModelFilterForm):
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=ContactGroup.objects.all(), queryset=ContactGroup.objects.all(),
required=False, required=False,
label=_('Parent group'), label=_('Parent group')
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -74,7 +71,6 @@ class ContactFilterForm(CustomFieldModelFilterForm):
queryset=ContactGroup.objects.all(), queryset=ContactGroup.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)

View File

@ -33,8 +33,7 @@ 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(),
@ -43,6 +42,5 @@ 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

@ -395,8 +395,8 @@ class DynamicModelChoiceMixin:
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, fetch_trigger=None, def __init__(self, query_params=None, initial_params=None, null_option=None, disabled_indicator=None,
empty_label=None, *args, **kwargs): fetch_trigger=None, empty_label=None, *args, **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

View File

@ -0,0 +1,5 @@
{% if value %}
<i class="mdi mdi-check-bold text-success" title="{{ true_label }}"></i>
{% elif show_false %}
<i class="mdi mdi-close-thick text-danger" title="{{ false_label }}"></i>
{% endif %}

View File

@ -426,6 +426,23 @@ def badge(value, bg_class='secondary', show_empty=False):
} }
@register.inclusion_tag('helpers/checkmark.html')
def checkmark(value, show_false=True, true='Yes', false='No'):
"""
Display either a green checkmark or red X to indicate a boolean value.
:param show_false: Display a red X if the value is False
:param true: Text label for true value
:param false: Text label for false value
"""
return {
'value': bool(value),
'show_false': show_false,
'true_label': true,
'false_label': false,
}
@register.inclusion_tag('helpers/table_config_form.html') @register.inclusion_tag('helpers/table_config_form.html')
def table_config_form(table, table_name=None): def table_config_form(table, table_name=None):
return { return {

View File

@ -40,20 +40,17 @@ class ClusterFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
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_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(),
@ -63,15 +60,13 @@ class ClusterFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
'region_id': '$region_id', 'region_id': '$region_id',
'site_group_id': '$site_group_id', 'site_group_id': '$site_group_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)
@ -89,33 +84,28 @@ class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm,
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(),
@ -125,8 +115,7 @@ class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm,
'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(),
@ -135,8 +124,7 @@ class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm,
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,
@ -147,8 +135,7 @@ class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm,
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,
@ -174,8 +161,7 @@ class VMInterfaceFilterForm(CustomFieldModelFilterForm):
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(),
@ -183,8 +169,7 @@ class VMInterfaceFilterForm(CustomFieldModelFilterForm):
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,

View File

@ -19,8 +19,7 @@ class WirelessLANGroupFilterForm(CustomFieldModelFilterForm):
parent_id = DynamicModelMultipleChoiceField( parent_id = DynamicModelMultipleChoiceField(
queryset=WirelessLANGroup.objects.all(), queryset=WirelessLANGroup.objects.all(),
required=False, required=False,
label=_('Parent group'), label=_('Parent group')
fetch_trigger='open'
) )
tag = TagFilterField(model) tag = TagFilterField(model)
@ -39,8 +38,7 @@ class WirelessLANFilterForm(CustomFieldModelFilterForm):
queryset=WirelessLANGroup.objects.all(), queryset=WirelessLANGroup.objects.all(),
required=False, required=False,
null_option='None', null_option='None',
label=_('Group'), label=_('Group')
fetch_trigger='open'
) )
auth_type = forms.ChoiceField( auth_type = forms.ChoiceField(
required=False, required=False,

View File

@ -6,7 +6,7 @@ django-graphiql-debug-toolbar==0.2.0
django-mptt==0.13.4 django-mptt==0.13.4
django-pglocks==1.0.4 django-pglocks==1.0.4
django-prometheus==2.2.0 django-prometheus==2.2.0
django-redis==5.1.0 django-redis==5.2.0
django-rq==2.5.1 django-rq==2.5.1
django-tables2==2.4.1 django-tables2==2.4.1
django-taggit==2.0.0 django-taggit==2.0.0
@ -21,7 +21,7 @@ markdown-include==0.6.0
mkdocs-material==8.1.3 mkdocs-material==8.1.3
netaddr==0.8.0 netaddr==0.8.0
Pillow==8.4.0 Pillow==8.4.0
psycopg2-binary==2.9.2 psycopg2-binary==2.9.3
PyYAML==6.0 PyYAML==6.0
social-auth-app-django==5.0.0 social-auth-app-django==5.0.0
social-auth-core==4.1.0 social-auth-core==4.1.0