diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index f13168f3c..23d5b8182 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.1.2 + placeholder: v3.1.3 validations: required: true - type: dropdown diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 277c9724f..00b464515 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -14,7 +14,7 @@ body: attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.1.2 + placeholder: v3.1.3 validations: required: true - type: dropdown diff --git a/docs/release-notes/version-3.1.md b/docs/release-notes/version-3.1.md index 2b8744032..61da98952 100644 --- a/docs/release-notes/version-3.1.md +++ b/docs/release-notes/version-3.1.md @@ -1,16 +1,23 @@ # NetBox v3.1 -## v3.1.3 (FUTURE) +## v3.1.4 (FUTURE) + +--- + +## v3.1.3 (2021-12-29) ### Enhancements * [#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 * [#8175](https://github.com/netbox-community/netbox/issues/8175) - Display parent object when attaching an image ### Bug Fixes * [#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 * [#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 diff --git a/netbox/circuits/forms/filtersets.py b/netbox/circuits/forms/filtersets.py index 68b57e03c..f5ff65088 100644 --- a/netbox/circuits/forms/filtersets.py +++ b/netbox/circuits/forms/filtersets.py @@ -26,14 +26,12 @@ class ProviderFilterForm(CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -42,8 +40,7 @@ class ProviderFilterForm(CustomFieldModelFilterForm): 'region_id': '$region_id', 'site_group_id': '$site_group_id', }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) asn = forms.IntegerField( required=False, @@ -61,8 +58,7 @@ class ProviderNetworkFilterForm(CustomFieldModelFilterForm): provider_id = DynamicModelMultipleChoiceField( queryset=Provider.objects.all(), required=False, - label=_('Provider'), - fetch_trigger='open' + label=_('Provider') ) service_id = forms.CharField( max_length=100, @@ -88,14 +84,12 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): type_id = DynamicModelMultipleChoiceField( queryset=CircuitType.objects.all(), required=False, - label=_('Type'), - fetch_trigger='open' + label=_('Type') ) provider_id = DynamicModelMultipleChoiceField( queryset=Provider.objects.all(), required=False, - label=_('Provider'), - fetch_trigger='open' + label=_('Provider') ) provider_network_id = DynamicModelMultipleChoiceField( queryset=ProviderNetwork.objects.all(), @@ -103,8 +97,7 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'provider_id': '$provider_id' }, - label=_('Provider network'), - fetch_trigger='open' + label=_('Provider network') ) status = forms.MultipleChoiceField( choices=CircuitStatusChoices, @@ -114,14 +107,12 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -130,8 +121,7 @@ class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): 'region_id': '$region_id', 'site_group_id': '$site_group_id', }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) commit_rate = forms.IntegerField( required=False, diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index c12891dc3..eb3035122 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/netbox/dcim/forms/filtersets.py @@ -62,14 +62,12 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -78,8 +76,7 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm): 'region_id': '$region_id', 'group_id': '$site_group_id', }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -87,14 +84,12 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id', }, - label=_('Location'), - fetch_trigger='open' + label=_('Location') ) virtual_chassis_id = DynamicModelMultipleChoiceField( queryset=VirtualChassis.objects.all(), required=False, - label=_('Virtual Chassis'), - fetch_trigger='open' + label=_('Virtual Chassis') ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -104,8 +99,7 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm): 'location_id': '$location_id', 'virtual_chassis_id': '$virtual_chassis_id' }, - label=_('Device'), - fetch_trigger='open' + label=_('Device') ) @@ -114,8 +108,7 @@ class RegionFilterForm(CustomFieldModelFilterForm): parent_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Parent region'), - fetch_trigger='open' + label=_('Parent region') ) tag = TagFilterField(model) @@ -125,8 +118,7 @@ class SiteGroupFilterForm(CustomFieldModelFilterForm): parent_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Parent group'), - fetch_trigger='open' + label=_('Parent group') ) tag = TagFilterField(model) @@ -147,20 +139,17 @@ class SiteFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) asn_id = DynamicModelMultipleChoiceField( queryset=ASN.objects.all(), required=False, - label=_('ASNs'), - fetch_trigger='open' + label=_('ASNs') ) tag = TagFilterField(model) @@ -175,14 +164,12 @@ class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -191,8 +178,7 @@ class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): 'region_id': '$region_id', 'group_id': '$site_group_id', }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) parent_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -201,8 +187,7 @@ class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): 'region_id': '$region_id', 'site_id': '$site_id', }, - label=_('Parent'), - fetch_trigger='open' + label=_('Parent') ) tag = TagFilterField(model) @@ -224,8 +209,7 @@ class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -233,8 +217,7 @@ class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -243,8 +226,7 @@ class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Location'), - fetch_trigger='open' + label=_('Location') ) status = forms.MultipleChoiceField( choices=RackStatusChoices, @@ -265,8 +247,7 @@ class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): queryset=RackRole.objects.all(), required=False, null_option='None', - label=_('Role'), - fetch_trigger='open' + label=_('Role') ) serial = forms.CharField( required=False @@ -285,8 +266,7 @@ class RackElevationFilterForm(RackFilterForm): query_params={ 'site_id': '$site_id', 'location_id': '$location_id', - }, - fetch_trigger='open' + } ) @@ -301,8 +281,7 @@ class RackReservationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -310,15 +289,13 @@ class RackReservationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.prefetch_related('site'), required=False, label=_('Location'), - null_option='None', - fetch_trigger='open' + null_option='None' ) user_id = DynamicModelMultipleChoiceField( queryset=User.objects.all(), @@ -326,8 +303,7 @@ class RackReservationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): label=_('User'), widget=APISelectMultiple( api_url='/api/users/users/', - ), - fetch_trigger='open' + ) ) tag = TagFilterField(model) @@ -347,8 +323,7 @@ class DeviceTypeFilterForm(CustomFieldModelFilterForm): manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer'), - fetch_trigger='open' + label=_('Manufacturer') ) part_number = forms.CharField( required=False @@ -479,8 +454,7 @@ class PlatformFilterForm(CustomFieldModelFilterForm): manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer'), - fetch_trigger='open' + label=_('Manufacturer') ) tag = TagFilterField(model) @@ -501,14 +475,12 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -517,8 +489,7 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi 'region_id': '$region_id', 'group_id': '$site_group_id', }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -527,8 +498,7 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi query_params={ 'site_id': '$site_id' }, - label=_('Location'), - fetch_trigger='open' + label=_('Location') ) rack_id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), @@ -538,20 +508,17 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi 'site_id': '$site_id', 'location_id': '$location_id', }, - label=_('Rack'), - fetch_trigger='open' + label=_('Rack') ) role_id = DynamicModelMultipleChoiceField( queryset=DeviceRole.objects.all(), required=False, - label=_('Role'), - fetch_trigger='open' + label=_('Role') ) manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer'), - fetch_trigger='open' + label=_('Manufacturer') ) device_type_id = DynamicModelMultipleChoiceField( queryset=DeviceType.objects.all(), @@ -559,15 +526,13 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi query_params={ 'manufacturer_id': '$manufacturer_id' }, - label=_('Model'), - fetch_trigger='open' + label=_('Model') ) platform_id = DynamicModelMultipleChoiceField( queryset=Platform.objects.all(), required=False, null_option='None', - label=_('Platform'), - fetch_trigger='open' + label=_('Platform') ) status = forms.MultipleChoiceField( choices=DeviceStatusChoices, @@ -689,14 +654,12 @@ class VirtualChassisFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -705,8 +668,7 @@ class VirtualChassisFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): 'region_id': '$region_id', 'group_id': '$site_group_id', }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) tag = TagFilterField(model) @@ -722,8 +684,7 @@ class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -731,8 +692,7 @@ class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) rack_id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), @@ -741,8 +701,7 @@ class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): null_option='None', query_params={ 'site_id': '$site_id' - }, - fetch_trigger='open' + } ) type = forms.MultipleChoiceField( choices=add_blank_choice(CableTypeChoices), @@ -765,8 +724,7 @@ class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): 'tenant_id': '$tenant_id', 'rack_id': '$rack_id', }, - label=_('Device'), - fetch_trigger='open' + label=_('Device') ) tag = TagFilterField(model) @@ -780,14 +738,12 @@ class PowerPanelFilterForm(CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -796,8 +752,7 @@ class PowerPanelFilterForm(CustomFieldModelFilterForm): 'region_id': '$region_id', 'group_id': '$site_group_id', }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) location_id = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), @@ -806,8 +761,7 @@ class PowerPanelFilterForm(CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Location'), - fetch_trigger='open' + label=_('Location') ) tag = TagFilterField(model) @@ -823,14 +777,12 @@ class PowerFeedFilterForm(CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -838,8 +790,7 @@ class PowerFeedFilterForm(CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) power_panel_id = DynamicModelMultipleChoiceField( queryset=PowerPanel.objects.all(), @@ -848,8 +799,7 @@ class PowerFeedFilterForm(CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Power panel'), - fetch_trigger='open' + label=_('Power panel') ) rack_id = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), @@ -858,8 +808,7 @@ class PowerFeedFilterForm(CustomFieldModelFilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Rack'), - fetch_trigger='open' + label=_('Rack') ) status = forms.MultipleChoiceField( choices=PowerFeedStatusChoices, @@ -1109,8 +1058,7 @@ class InventoryItemFilterForm(DeviceComponentFilterForm): manufacturer_id = DynamicModelMultipleChoiceField( queryset=Manufacturer.objects.all(), required=False, - label=_('Manufacturer'), - fetch_trigger='open' + label=_('Manufacturer') ) serial = forms.CharField( required=False @@ -1144,8 +1092,7 @@ class ConsoleConnectionFilterForm(FilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -1153,8 +1100,7 @@ class ConsoleConnectionFilterForm(FilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -1162,8 +1108,7 @@ class ConsoleConnectionFilterForm(FilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Device'), - fetch_trigger='open' + label=_('Device') ) @@ -1171,8 +1116,7 @@ class PowerConnectionFilterForm(FilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -1180,8 +1124,7 @@ class PowerConnectionFilterForm(FilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -1189,8 +1132,7 @@ class PowerConnectionFilterForm(FilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Device'), - fetch_trigger='open' + label=_('Device') ) @@ -1198,8 +1140,7 @@ class InterfaceConnectionFilterForm(FilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -1207,8 +1148,7 @@ class InterfaceConnectionFilterForm(FilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) device_id = DynamicModelMultipleChoiceField( queryset=Device.objects.all(), @@ -1216,6 +1156,5 @@ class InterfaceConnectionFilterForm(FilterForm): query_params={ 'site_id': '$site_id' }, - label=_('Device'), - fetch_trigger='open' + label=_('Device') ) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index 762924653..e2c343028 100644 --- a/netbox/dcim/forms/models.py +++ b/netbox/dcim/forms/models.py @@ -300,16 +300,14 @@ class RackReservationForm(TenancyForm, CustomFieldModelForm): required=False, initial_params={ 'sites': '$site' - }, - fetch_trigger='open' + } ) site_group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' - }, - fetch_trigger='open' + } ) site = DynamicModelChoiceField( queryset=Site.objects.all(), @@ -317,24 +315,21 @@ class RackReservationForm(TenancyForm, CustomFieldModelForm): query_params={ 'region_id': '$region', 'group_id': '$site_group', - }, - fetch_trigger='open' + } ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' - }, - fetch_trigger='open' + } ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), query_params={ 'site_id': '$site', 'location_id': '$location', - }, - fetch_trigger='open' + } ) units = NumericArrayField( base_field=forms.IntegerField(), @@ -348,8 +343,7 @@ class RackReservationForm(TenancyForm, CustomFieldModelForm): ) tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), - required=False, - fetch_trigger='open' + required=False ) class Meta: diff --git a/netbox/extras/forms/bulk_import.py b/netbox/extras/forms/bulk_import.py index fb8cf53e8..9f44494e0 100644 --- a/netbox/extras/forms/bulk_import.py +++ b/netbox/extras/forms/bulk_import.py @@ -3,9 +3,10 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.forms import SimpleArrayField from django.utils.safestring import mark_safe +from extras.choices import CustomFieldTypeChoices from extras.models import * from extras.utils import FeatureQuery -from utilities.forms import CSVContentTypeField, CSVModelForm, CSVMultipleContentTypeField, SlugField +from utilities.forms import CSVChoiceField, CSVContentTypeField, CSVModelForm, CSVMultipleContentTypeField, SlugField __all__ = ( 'CustomFieldCSVForm', @@ -22,6 +23,10 @@ class CustomFieldCSVForm(CSVModelForm): limit_choices_to=FeatureQuery('custom_fields'), help_text="One or more assigned object types" ) + type = CSVChoiceField( + choices=CustomFieldTypeChoices, + help_text='Field data type (e.g. text, integer, etc.)' + ) choices = SimpleArrayField( base_field=forms.CharField(), required=False, @@ -32,7 +37,7 @@ class CustomFieldCSVForm(CSVModelForm): model = CustomField fields = ( 'name', 'label', 'type', 'content_types', 'required', 'description', 'weight', 'filter_logic', 'default', - 'choices', 'weight', + 'choices', 'weight', 'validation_minimum', 'validation_maximum', 'validation_regex', ) diff --git a/netbox/extras/forms/filtersets.py b/netbox/extras/forms/filtersets.py index 29527c20e..388cd1e60 100644 --- a/netbox/extras/forms/filtersets.py +++ b/netbox/extras/forms/filtersets.py @@ -164,38 +164,32 @@ class ConfigContextFilterForm(FilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Regions'), - fetch_trigger='open' + label=_('Regions') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site groups'), - fetch_trigger='open' + label=_('Site groups') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), required=False, - label=_('Sites'), - fetch_trigger='open' + label=_('Sites') ) device_type_id = DynamicModelMultipleChoiceField( queryset=DeviceType.objects.all(), required=False, - label=_('Device types'), - fetch_trigger='open' + label=_('Device types') ) role_id = DynamicModelMultipleChoiceField( queryset=DeviceRole.objects.all(), required=False, - label=_('Roles'), - fetch_trigger='open' + label=_('Roles') ) platform_id = DynamicModelMultipleChoiceField( queryset=Platform.objects.all(), required=False, - label=_('Platforms'), - fetch_trigger='open' + label=_('Platforms') ) cluster_type_id = DynamicModelMultipleChoiceField( queryset=ClusterType.objects.all(), @@ -206,33 +200,28 @@ class ConfigContextFilterForm(FilterForm): cluster_group_id = DynamicModelMultipleChoiceField( queryset=ClusterGroup.objects.all(), required=False, - label=_('Cluster groups'), - fetch_trigger='open' + label=_('Cluster groups') ) cluster_id = DynamicModelMultipleChoiceField( queryset=Cluster.objects.all(), required=False, - label=_('Clusters'), - fetch_trigger='open' + label=_('Clusters') ) tenant_group_id = DynamicModelMultipleChoiceField( queryset=TenantGroup.objects.all(), required=False, - label=_('Tenant groups'), - fetch_trigger='open' + label=_('Tenant groups') ) tenant_id = DynamicModelMultipleChoiceField( queryset=Tenant.objects.all(), required=False, - label=_('Tenant'), - fetch_trigger='open' + label=_('Tenant') ) tag = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), to_field_name='slug', required=False, - label=_('Tags'), - fetch_trigger='open' + label=_('Tags') ) @@ -269,8 +258,7 @@ class JournalEntryFilterForm(FilterForm): label=_('User'), widget=APISelectMultiple( api_url='/api/users/users/', - ), - fetch_trigger='open' + ) ) assigned_object_type_id = DynamicModelMultipleChoiceField( queryset=ContentType.objects.all(), @@ -278,8 +266,7 @@ class JournalEntryFilterForm(FilterForm): label=_('Object Type'), widget=APISelectMultiple( api_url='/api/extras/content-types/', - ), - fetch_trigger='open' + ) ) kind = forms.ChoiceField( choices=add_blank_choice(JournalEntryKindChoices), @@ -316,8 +303,7 @@ class ObjectChangeFilterForm(FilterForm): label=_('User'), widget=APISelectMultiple( api_url='/api/users/users/', - ), - fetch_trigger='open' + ) ) changed_object_type_id = DynamicModelMultipleChoiceField( queryset=ContentType.objects.all(), @@ -325,6 +311,5 @@ class ObjectChangeFilterForm(FilterForm): label=_('Object Type'), widget=APISelectMultiple( api_url='/api/extras/content-types/', - ), - fetch_trigger='open' + ) ) diff --git a/netbox/extras/tests/test_views.py b/netbox/extras/tests/test_views.py index 9ce324a5c..67abcf543 100644 --- a/netbox/extras/tests/test_views.py +++ b/netbox/extras/tests/test_views.py @@ -39,10 +39,10 @@ class CustomFieldTestCase(ViewTestCases.PrimaryObjectViewTestCase): } cls.csv_data = ( - 'name,label,type,content_types,weight,filter_logic,choices', - 'field4,Field 4,text,dcim.site,100,exact,', - 'field5,Field 5,integer,dcim.site,100,exact,', - 'field6,Field 6,select,dcim.site,100,exact,"A,B,C"', + 'name,label,type,content_types,weight,filter_logic,choices,validation_minimum,validation_maximum,validation_regex', + 'field4,Field 4,text,dcim.site,100,exact,,,,[a-z]{3}', + 'field5,Field 5,integer,dcim.site,100,exact,,1,100,', + 'field6,Field 6,select,dcim.site,100,exact,"A,B,C",,,', ) cls.bulk_edit_data = { diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index a7732fe9a..df95bdd05 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -48,14 +48,12 @@ class VRFFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): import_target_id = DynamicModelMultipleChoiceField( queryset=RouteTarget.objects.all(), required=False, - label=_('Import targets'), - fetch_trigger='open' + label=_('Import targets') ) export_target_id = DynamicModelMultipleChoiceField( queryset=RouteTarget.objects.all(), required=False, - label=_('Export targets'), - fetch_trigger='open' + label=_('Export targets') ) tag = TagFilterField(model) @@ -70,14 +68,12 @@ class RouteTargetFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): importing_vrf_id = DynamicModelMultipleChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Imported by VRF'), - fetch_trigger='open' + label=_('Imported by VRF') ) exporting_vrf_id = DynamicModelMultipleChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Exported by VRF'), - fetch_trigger='open' + label=_('Exported by VRF') ) tag = TagFilterField(model) @@ -110,8 +106,7 @@ class AggregateFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): rir_id = DynamicModelMultipleChoiceField( queryset=RIR.objects.all(), required=False, - label=_('RIR'), - fetch_trigger='open' + label=_('RIR') ) tag = TagFilterField(model) @@ -127,14 +122,12 @@ class ASNFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): rir_id = DynamicModelMultipleChoiceField( queryset=RIR.objects.all(), required=False, - label=_('RIR'), - fetch_trigger='open' + label=_('RIR') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), required=False, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) @@ -180,14 +173,12 @@ class PrefixFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): queryset=VRF.objects.all(), required=False, label=_('Assigned VRF'), - null_option='Global', - fetch_trigger='open' + null_option='Global' ) present_in_vrf_id = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Present in VRF'), - fetch_trigger='open' + label=_('Present in VRF') ) status = forms.MultipleChoiceField( choices=PrefixStatusChoices, @@ -197,14 +188,12 @@ class PrefixFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -213,15 +202,13 @@ class PrefixFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'region_id': '$region_id' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) role_id = DynamicModelMultipleChoiceField( queryset=Role.objects.all(), required=False, null_option='None', - label=_('Role'), - fetch_trigger='open' + label=_('Role') ) is_pool = forms.NullBooleanField( required=False, @@ -257,8 +244,7 @@ class IPRangeFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): queryset=VRF.objects.all(), required=False, label=_('Assigned VRF'), - null_option='Global', - fetch_trigger='open' + null_option='Global' ) status = forms.MultipleChoiceField( choices=PrefixStatusChoices, @@ -269,8 +255,7 @@ class IPRangeFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): queryset=Role.objects.all(), required=False, null_option='None', - label=_('Role'), - fetch_trigger='open' + label=_('Role') ) tag = TagFilterField(model) @@ -308,14 +293,12 @@ class IPAddressFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): queryset=VRF.objects.all(), required=False, label=_('Assigned VRF'), - null_option='Global', - fetch_trigger='open' + null_option='Global' ) present_in_vrf_id = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, - label=_('Present in VRF'), - fetch_trigger='open' + label=_('Present in VRF') ) status = forms.MultipleChoiceField( choices=IPAddressStatusChoices, @@ -377,32 +360,27 @@ class VLANGroupFilterForm(CustomFieldModelFilterForm): region = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) sitegroup = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), required=False, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) location = DynamicModelMultipleChoiceField( queryset=Location.objects.all(), required=False, - label=_('Location'), - fetch_trigger='open' + label=_('Location') ) rack = DynamicModelMultipleChoiceField( queryset=Rack.objects.all(), required=False, - label=_('Rack'), - fetch_trigger='open' + label=_('Rack') ) min_vid = forms.IntegerField( min_value=VLAN_VID_MIN, @@ -426,14 +404,12 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): region_id = DynamicModelMultipleChoiceField( queryset=Region.objects.all(), required=False, - label=_('Region'), - fetch_trigger='open' + label=_('Region') ) site_group_id = DynamicModelMultipleChoiceField( queryset=SiteGroup.objects.all(), required=False, - label=_('Site group'), - fetch_trigger='open' + label=_('Site group') ) site_id = DynamicModelMultipleChoiceField( queryset=Site.objects.all(), @@ -442,8 +418,7 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'region': '$region' }, - label=_('Site'), - fetch_trigger='open' + label=_('Site') ) group_id = DynamicModelMultipleChoiceField( queryset=VLANGroup.objects.all(), @@ -452,8 +427,7 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): query_params={ 'region': '$region' }, - label=_('VLAN group'), - fetch_trigger='open' + label=_('VLAN group') ) status = forms.MultipleChoiceField( choices=VLANStatusChoices, @@ -464,8 +438,7 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm): queryset=Role.objects.all(), required=False, null_option='None', - label=_('Role'), - fetch_trigger='open' + label=_('Role') ) vid = forms.IntegerField( required=False, diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 4c93ee982..05aee2606 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -418,7 +418,7 @@ class PrefixView(generic.ObjectView): ).filter( prefix__net_contains=str(instance.prefix) ).prefetch_related( - 'site', 'role' + 'site', 'role', 'tenant' ) parent_prefix_table = tables.PrefixTable( list(parent_prefixes), diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index d9b437531..9e8ae3085 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/src/select/api/apiSelect.ts b/netbox/project-static/src/select/api/apiSelect.ts index 032fc83fa..f24c3fa5b 100644 --- a/netbox/project-static/src/select/api/apiSelect.ts +++ b/netbox/project-static/src/select/api/apiSelect.ts @@ -251,7 +251,7 @@ export class APISelect { } else if (collapse !== null) { this.trigger = 'collapse'; } else { - this.trigger = 'load'; + this.trigger = 'open'; } switch (this.trigger) { diff --git a/netbox/templates/base/layout.html b/netbox/templates/base/layout.html index a207558cc..7b1597bf0 100644 --- a/netbox/templates/base/layout.html +++ b/netbox/templates/base/layout.html @@ -20,7 +20,7 @@ {# Top bar #} -