diff --git a/netbox/circuits/forms.py b/netbox/circuits/forms.py index 77683e530..0b0378a7a 100644 --- a/netbox/circuits/forms.py +++ b/netbox/circuits/forms.py @@ -9,7 +9,8 @@ from tenancy.forms import TenancyFilterForm, TenancyForm from tenancy.models import Tenant from utilities.forms import ( APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, CommentField, CSVChoiceField, DatePicker, - DynamicModelMultipleChoiceField, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField, + DynamicModelChoiceField, DynamicModelMultipleChoiceField, SmallTextarea, SlugField, StaticSelect2, + StaticSelect2Multiple, TagFilterField, ) from .choices import CircuitStatusChoices from .models import Circuit, CircuitTermination, CircuitType, Provider @@ -165,6 +166,18 @@ class CircuitTypeCSVForm(forms.ModelForm): # class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): + provider = DynamicModelChoiceField( + queryset=Provider.objects.all(), + widget=APISelect( + api_url="/api/circuits/providers/" + ) + ) + type = DynamicModelChoiceField( + queryset=CircuitType.objects.all(), + widget=APISelect( + api_url="/api/circuits/circuit-types/" + ) + ) comments = CommentField() tags = TagField( required=False @@ -181,12 +194,6 @@ class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): 'commit_rate': "Committed rate", } widgets = { - 'provider': APISelect( - api_url="/api/circuits/providers/" - ), - 'type': APISelect( - api_url="/api/circuits/circuit-types/" - ), 'status': StaticSelect2(), 'install_date': DatePicker(), } @@ -236,14 +243,14 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit queryset=Circuit.objects.all(), widget=forms.MultipleHiddenInput ) - type = forms.ModelChoiceField( + type = DynamicModelChoiceField( queryset=CircuitType.objects.all(), required=False, widget=APISelect( api_url="/api/circuits/circuit-types/" ) ) - provider = forms.ModelChoiceField( + provider = DynamicModelChoiceField( queryset=Provider.objects.all(), required=False, widget=APISelect( @@ -256,7 +263,7 @@ class CircuitBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit initial='', widget=StaticSelect2() ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 4de1c1eca..99bb470f8 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -325,7 +325,7 @@ class SiteBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor required=False, widget=StaticSelect2() ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( @@ -383,18 +383,20 @@ class SiteFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm): # class RackGroupForm(BootstrapMixin, forms.ModelForm): + site = DynamicModelChoiceField( + queryset=Site.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/sites/" + ) + ) slug = SlugField() class Meta: model = RackGroup - fields = [ + fields = ( 'site', 'name', 'slug', - ] - widgets = { - 'site': APISelect( - api_url="/api/dcim/sites/" - ) - } + ) class RackGroupCSVForm(forms.ModelForm): @@ -471,6 +473,15 @@ class RackRoleCSVForm(forms.ModelForm): # class RackForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): + site = DynamicModelChoiceField( + queryset=Site.objects.all(), + widget=APISelect( + api_url="/api/dcim/sites/", + filter_for={ + 'group': 'site_id', + } + ) + ) group = DynamicModelChoiceField( queryset=RackGroup.objects.all(), required=False, @@ -478,6 +489,13 @@ class RackForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): api_url='/api/dcim/rack-groups/', ) ) + role = DynamicModelChoiceField( + queryset=RackRole.objects.all(), + required=False, + widget=APISelect( + api_url='/api/dcim/rack-roles/', + ) + ) comments = CommentField() tags = TagField( required=False @@ -496,16 +514,7 @@ class RackForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): 'u_height': "Height in rack units", } widgets = { - 'site': APISelect( - api_url="/api/dcim/sites/", - filter_for={ - 'group': 'site_id', - } - ), 'status': StaticSelect2(), - 'role': APISelect( - api_url="/api/dcim/rack-roles/" - ), 'type': StaticSelect2(), 'width': StaticSelect2(), 'outer_unit': StaticSelect2(), @@ -605,7 +614,7 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor queryset=Rack.objects.all(), widget=forms.MultipleHiddenInput ) - site = forms.ModelChoiceField( + site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, widget=APISelect( @@ -615,14 +624,14 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor } ) ) - group = forms.ModelChoiceField( + group = DynamicModelChoiceField( queryset=RackGroup.objects.all(), required=False, widget=APISelect( api_url="/api/dcim/rack-groups", ) ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( @@ -635,7 +644,7 @@ class RackBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor initial='', widget=StaticSelect2() ) - role = forms.ModelChoiceField( + role = DynamicModelChoiceField( queryset=RackRole.objects.all(), required=False, widget=APISelect( @@ -833,7 +842,7 @@ class RackReservationBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( @@ -905,6 +914,12 @@ class ManufacturerCSVForm(forms.ModelForm): # class DeviceTypeForm(BootstrapMixin, CustomFieldModelForm): + manufacturer = DynamicModelChoiceField( + queryset=Manufacturer.objects.all(), + widget=APISelect( + api_url="/api/dcim/manufacturers/", + ) + ) slug = SlugField( slug_source='model' ) @@ -920,9 +935,6 @@ class DeviceTypeForm(BootstrapMixin, CustomFieldModelForm): 'tags', ] widgets = { - 'manufacturer': APISelect( - api_url="/api/dcim/manufacturers/" - ), 'subdevice_role': StaticSelect2() } @@ -945,11 +957,11 @@ class DeviceTypeBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkE queryset=DeviceType.objects.all(), widget=forms.MultipleHiddenInput() ) - manufacturer = forms.ModelChoiceField( + manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False, widget=APISelect( - api_url="/api/dcim/manufactureres" + api_url="/api/dcim/manufacturers" ) ) u_height = forms.IntegerField( @@ -1048,7 +1060,7 @@ class ConsolePortTemplateForm(BootstrapMixin, forms.ModelForm): class ConsolePortTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1091,7 +1103,7 @@ class ConsoleServerPortTemplateForm(BootstrapMixin, forms.ModelForm): class ConsoleServerPortTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1134,7 +1146,7 @@ class PowerPortTemplateForm(BootstrapMixin, forms.ModelForm): class PowerPortTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1207,7 +1219,7 @@ class PowerOutletTemplateForm(BootstrapMixin, forms.ModelForm): class PowerOutletTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1276,7 +1288,7 @@ class InterfaceTemplateForm(BootstrapMixin, forms.ModelForm): class InterfaceTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1339,7 +1351,7 @@ class FrontPortTemplateForm(BootstrapMixin, forms.ModelForm): class FrontPortTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1433,7 +1445,7 @@ class RearPortTemplateForm(BootstrapMixin, forms.ModelForm): class RearPortTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1482,7 +1494,7 @@ class DeviceBayTemplateForm(BootstrapMixin, forms.ModelForm): class DeviceBayTemplateCreateForm(BootstrapMixin, forms.Form): - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), widget=APISelect( api_url='/api/dcim/device-types/' @@ -1653,6 +1665,13 @@ class DeviceRoleCSVForm(forms.ModelForm): # class PlatformForm(BootstrapMixin, forms.ModelForm): + manufacturer = DynamicModelChoiceField( + queryset=Manufacturer.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/manufacturers/", + ) + ) slug = SlugField( max_length=64 ) @@ -1663,9 +1682,6 @@ class PlatformForm(BootstrapMixin, forms.ModelForm): 'name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', ] widgets = { - 'manufacturer': APISelect( - api_url="/api/dcim/manufacturers/" - ), 'napalm_args': SmallTextarea(), } @@ -1695,7 +1711,7 @@ class PlatformCSVForm(forms.ModelForm): # class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): - site = forms.ModelChoiceField( + site = DynamicModelChoiceField( queryset=Site.objects.all(), widget=APISelect( api_url="/api/dcim/sites/", @@ -1721,7 +1737,7 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): disabled_indicator='device' ) ) - manufacturer = forms.ModelChoiceField( + manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False, widget=APISelect( @@ -1734,13 +1750,28 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): ) device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), - label='Device type', widget=APISelect( api_url='/api/dcim/device-types/', display_field='model' ) ) - cluster_group = forms.ModelChoiceField( + device_role = DynamicModelChoiceField( + queryset=DeviceRole.objects.all(), + widget=APISelect( + api_url='/api/dcim/device-roles/' + ) + ) + platform = DynamicModelChoiceField( + queryset=Platform.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/platforms/", + additional_query_params={ + "manufacturer_id": "null" + } + ) + ) + cluster_group = DynamicModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=APISelect( @@ -1786,16 +1817,7 @@ class DeviceForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): 'position': 'face' } ), - 'device_role': APISelect( - api_url='/api/dcim/device-roles/' - ), 'status': StaticSelect2(), - 'platform': APISelect( - api_url="/api/dcim/platforms/", - additional_query_params={ - "manufacturer_id": "null" - } - ), 'primary_ip4': StaticSelect2(), 'primary_ip6': StaticSelect2(), } @@ -2069,31 +2091,29 @@ class DeviceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF queryset=Device.objects.all(), widget=forms.MultipleHiddenInput() ) - device_type = forms.ModelChoiceField( + device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), required=False, - label='Type', widget=APISelect( api_url="/api/dcim/device-types/", display_field='display_name' ) ) - device_role = forms.ModelChoiceField( + device_role = DynamicModelChoiceField( queryset=DeviceRole.objects.all(), required=False, - label='Role', widget=APISelect( api_url="/api/dcim/device-roles/" ) ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( api_url="/api/tenancy/tenants/" ) ) - platform = forms.ModelChoiceField( + platform = DynamicModelChoiceField( queryset=Platform.objects.all(), required=False, widget=APISelect( @@ -2103,7 +2123,6 @@ class DeviceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF status = forms.ChoiceField( choices=add_blank_choice(DeviceStatusChoices), required=False, - initial='', widget=StaticSelect2() ) serial = forms.CharField( @@ -2345,7 +2364,7 @@ class ConsolePortForm(BootstrapMixin, forms.ModelForm): class ConsolePortCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -2430,7 +2449,7 @@ class ConsoleServerPortForm(BootstrapMixin, forms.ModelForm): class ConsoleServerPortCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -2529,7 +2548,7 @@ class PowerPortForm(BootstrapMixin, forms.ModelForm): class PowerPortCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -2647,7 +2666,7 @@ class PowerOutletForm(BootstrapMixin, forms.ModelForm): class PowerOutletCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -2806,25 +2825,26 @@ class InterfaceFilterForm(DeviceComponentFilterForm): class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm): - untagged_vlan = forms.ModelChoiceField( + untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, + label='Untagged VLAN', widget=APISelect( api_url="/api/ipam/vlans/", display_field='display_name', full=True ) ) - tagged_vlans = forms.ModelMultipleChoiceField( + tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, + label='Tagged VLANs', widget=APISelectMultiple( api_url="/api/ipam/vlans/", display_field='display_name', full=True ) ) - tags = TagField( required=False ) @@ -2866,7 +2886,7 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm): class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -2916,7 +2936,7 @@ class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form): tags = TagField( required=False ) - untagged_vlan = forms.ModelChoiceField( + untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelect( @@ -2925,7 +2945,7 @@ class InterfaceCreateForm(BootstrapMixin, InterfaceCommonForm, forms.Form): full=True ) ) - tagged_vlans = forms.ModelMultipleChoiceField( + tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple( @@ -3064,7 +3084,7 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): required=False, widget=StaticSelect2() ) - untagged_vlan = forms.ModelChoiceField( + untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelect( @@ -3073,7 +3093,7 @@ class InterfaceBulkEditForm(BootstrapMixin, AddRemoveTagsForm, BulkEditForm): full=True ) ) - tagged_vlans = forms.ModelMultipleChoiceField( + tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple( @@ -3166,7 +3186,7 @@ class FrontPortForm(BootstrapMixin, forms.ModelForm): # TODO: Merge with FrontPortTemplateCreateForm to remove duplicate logic class FrontPortCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -3344,7 +3364,7 @@ class RearPortForm(BootstrapMixin, forms.ModelForm): class RearPortCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -3429,7 +3449,7 @@ class ConnectCableToDeviceForm(BootstrapMixin, forms.ModelForm): """ Base form for connecting a Cable to a Device component """ - termination_b_site = forms.ModelChoiceField( + termination_b_site = DynamicModelChoiceField( queryset=Site.objects.all(), label='Site', required=False, @@ -3555,7 +3575,7 @@ class ConnectCableToRearPortForm(ConnectCableToDeviceForm): class ConnectCableToCircuitTerminationForm(BootstrapMixin, forms.ModelForm): - termination_b_provider = forms.ModelChoiceField( + termination_b_provider = DynamicModelChoiceField( queryset=Provider.objects.all(), label='Provider', required=False, @@ -3606,7 +3626,7 @@ class ConnectCableToCircuitTerminationForm(BootstrapMixin, forms.ModelForm): class ConnectCableToPowerFeedForm(BootstrapMixin, forms.ModelForm): - termination_b_site = forms.ModelChoiceField( + termination_b_site = DynamicModelChoiceField( queryset=Site.objects.all(), label='Site', required=False, @@ -3943,7 +3963,7 @@ class DeviceBayForm(BootstrapMixin, forms.ModelForm): class DeviceBayCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -4113,6 +4133,19 @@ class InterfaceConnectionFilterForm(BootstrapMixin, forms.Form): # class InventoryItemForm(BootstrapMixin, forms.ModelForm): + device = DynamicModelChoiceField( + queryset=Device.objects.prefetch_related('device_type__manufacturer'), + widget=APISelect( + api_url="/api/dcim/devices/" + ) + ) + manufacturer = DynamicModelChoiceField( + queryset=Manufacturer.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/manufacturers/" + ) + ) tags = TagField( required=False ) @@ -4122,18 +4155,10 @@ class InventoryItemForm(BootstrapMixin, forms.ModelForm): fields = [ 'name', 'device', 'manufacturer', 'part_id', 'serial', 'asset_tag', 'description', 'tags', ] - widgets = { - 'device': APISelect( - api_url="/api/dcim/devices/" - ), - 'manufacturer': APISelect( - api_url="/api/dcim/manufacturers/" - ) - } class InventoryItemCreateForm(BootstrapMixin, forms.Form): - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.prefetch_related('device_type__manufacturer'), widget=APISelect( api_url="/api/dcim/devices/", @@ -4142,7 +4167,7 @@ class InventoryItemCreateForm(BootstrapMixin, forms.Form): name_pattern = ExpandableNameField( label='Name' ) - manufacturer = forms.ModelChoiceField( + manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False, widget=APISelect( @@ -4197,14 +4222,14 @@ class InventoryItemBulkEditForm(BootstrapMixin, BulkEditForm): queryset=InventoryItem.objects.all(), widget=forms.MultipleHiddenInput() ) - device = forms.ModelChoiceField( + device = DynamicModelChoiceField( queryset=Device.objects.all(), required=False, widget=APISelect( api_url="/api/dcim/devices/" ) ) - manufacturer = forms.ModelChoiceField( + manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False, widget=APISelect( @@ -4364,7 +4389,7 @@ class DeviceVCMembershipForm(forms.ModelForm): class VCMemberSelectForm(BootstrapMixin, forms.Form): - site = forms.ModelChoiceField( + site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, widget=APISelect( @@ -4466,6 +4491,16 @@ class VirtualChassisFilterForm(BootstrapMixin, CustomFieldFilterForm): # class PowerPanelForm(BootstrapMixin, forms.ModelForm): + site = DynamicModelChoiceField( + queryset=Site.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/sites/", + filter_for={ + 'rack_group': 'site_id', + } + ) + ) rack_group = DynamicModelChoiceField( queryset=RackGroup.objects.all(), required=False, @@ -4479,14 +4514,6 @@ class PowerPanelForm(BootstrapMixin, forms.ModelForm): fields = [ 'site', 'rack_group', 'name', ] - widgets = { - 'site': APISelect( - api_url="/api/dcim/sites/", - filter_for={ - 'rack_group': 'site_id', - } - ), - } class PowerPanelCSVForm(forms.ModelForm): @@ -4581,6 +4608,19 @@ class PowerFeedForm(BootstrapMixin, CustomFieldModelForm): } ) ) + power_panel = DynamicModelChoiceField( + queryset=PowerPanel.objects.all(), + widget=APISelect( + api_url="/api/dcim/power-panels/" + ) + ) + rack = DynamicModelChoiceField( + queryset=Rack.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/racks/" + ) + ) comments = CommentField() tags = TagField( required=False @@ -4593,12 +4633,6 @@ class PowerFeedForm(BootstrapMixin, CustomFieldModelForm): 'max_utilization', 'comments', 'tags', ] widgets = { - 'power_panel': APISelect( - api_url="/api/dcim/power-panels/" - ), - 'rack': APISelect( - api_url="/api/dcim/racks/" - ), 'status': StaticSelect2(), 'type': StaticSelect2(), 'supply': StaticSelect2(), @@ -4697,7 +4731,7 @@ class PowerFeedBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd queryset=PowerFeed.objects.all(), widget=forms.MultipleHiddenInput ) - power_panel = forms.ModelChoiceField( + power_panel = DynamicModelChoiceField( queryset=PowerPanel.objects.all(), required=False, widget=APISelect( @@ -4707,7 +4741,7 @@ class PowerFeedBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd } ) ) - rack = forms.ModelChoiceField( + rack = DynamicModelChoiceField( queryset=Rack.objects.all(), required=False, widget=APISelect( diff --git a/netbox/extras/forms.py b/netbox/extras/forms.py index 45a42135d..d6a5406b7 100644 --- a/netbox/extras/forms.py +++ b/netbox/extras/forms.py @@ -196,7 +196,56 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm): required=False, widget=StaticSelect2Multiple() ) - tags = forms.ModelMultipleChoiceField( + sites = DynamicModelMultipleChoiceField( + queryset=Site.objects.all(), + required=False, + widget=APISelectMultiple( + api_url="/api/dcim/sites/" + ) + ) + roles = DynamicModelMultipleChoiceField( + queryset=DeviceRole.objects.all(), + required=False, + widget=APISelectMultiple( + api_url="/api/dcim/device-roles/" + ) + ) + platforms = DynamicModelMultipleChoiceField( + queryset=Platform.objects.all(), + required=False, + widget=APISelectMultiple( + api_url="/api/dcim/platforms/" + ) + ) + cluster_groups = DynamicModelMultipleChoiceField( + queryset=ClusterGroup.objects.all(), + required=False, + widget=APISelectMultiple( + api_url="/api/virtualization/cluster-groups/" + ) + ) + clusters = DynamicModelMultipleChoiceField( + queryset=Cluster.objects.all(), + required=False, + widget=APISelectMultiple( + api_url="/api/virtualization/clusters/" + ) + ) + tenant_groups = DynamicModelMultipleChoiceField( + queryset=TenantGroup.objects.all(), + required=False, + widget=APISelectMultiple( + api_url="/api/tenancy/tenant-groups/" + ) + ) + tenants = DynamicModelMultipleChoiceField( + queryset=Tenant.objects.all(), + required=False, + widget=APISelectMultiple( + api_url="/api/tenancy/tenants/" + ) + ) + tags = DynamicModelMultipleChoiceField( queryset=Tag.objects.all(), to_field_name='slug', required=False, @@ -210,36 +259,10 @@ class ConfigContextForm(BootstrapMixin, forms.ModelForm): class Meta: model = ConfigContext - fields = [ + fields = ( 'name', 'weight', 'description', 'is_active', 'regions', 'sites', 'roles', 'platforms', 'cluster_groups', 'clusters', 'tenant_groups', 'tenants', 'tags', 'data', - ] - widgets = { - 'regions': APISelectMultiple( - api_url="/api/dcim/regions/" - ), - 'sites': APISelectMultiple( - api_url="/api/dcim/sites/" - ), - 'roles': APISelectMultiple( - api_url="/api/dcim/device-roles/" - ), - 'platforms': APISelectMultiple( - api_url="/api/dcim/platforms/" - ), - 'cluster_groups': APISelectMultiple( - api_url="/api/virtualization/cluster-groups/" - ), - 'clusters': APISelectMultiple( - api_url="/api/virtualization/clusters/" - ), - 'tenant_groups': APISelectMultiple( - api_url="/api/tenancy/tenant-groups/" - ), - 'tenants': APISelectMultiple( - api_url="/api/tenancy/tenants/" - ), - } + ) class ConfigContextBulkEditForm(BootstrapMixin, BulkEditForm): diff --git a/netbox/ipam/forms.py b/netbox/ipam/forms.py index 6a65c4e1d..2b7fb2a6b 100644 --- a/netbox/ipam/forms.py +++ b/netbox/ipam/forms.py @@ -76,7 +76,7 @@ class VRFBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditForm queryset=VRF.objects.all(), widget=forms.MultipleHiddenInput() ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( @@ -149,6 +149,12 @@ class RIRFilterForm(BootstrapMixin, forms.Form): # class AggregateForm(BootstrapMixin, CustomFieldModelForm): + rir = DynamicModelChoiceField( + queryset=RIR.objects.all(), + widget=APISelect( + api_url="/api/ipam/rirs/" + ) + ) tags = TagField( required=False ) @@ -163,9 +169,6 @@ class AggregateForm(BootstrapMixin, CustomFieldModelForm): 'rir': "Regional Internet Registry responsible for this prefix", } widgets = { - 'rir': APISelect( - api_url="/api/ipam/rirs/" - ), 'date_added': DatePicker(), } @@ -190,7 +193,7 @@ class AggregateBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd queryset=Aggregate.objects.all(), widget=forms.MultipleHiddenInput() ) - rir = forms.ModelChoiceField( + rir = DynamicModelChoiceField( queryset=RIR.objects.all(), required=False, label='RIR', @@ -270,7 +273,14 @@ class RoleCSVForm(forms.ModelForm): # class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): - site = forms.ModelChoiceField( + vrf = DynamicModelChoiceField( + queryset=VRF.objects.all(), + required=False, + widget=APISelect( + api_url="/api/ipam/vrfs/", + ) + ) + site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, widget=APISelect( @@ -307,6 +317,13 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): display_field='display_name' ) ) + role = DynamicModelChoiceField( + queryset=Role.objects.all(), + required=False, + widget=APISelect( + api_url="/api/ipam/roles/" + ) + ) tags = TagField(required=False) class Meta: @@ -316,13 +333,7 @@ class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): 'tags', ] widgets = { - 'vrf': APISelect( - api_url="/api/ipam/vrfs/" - ), 'status': StaticSelect2(), - 'role': APISelect( - api_url="/api/ipam/roles/" - ) } def __init__(self, *args, **kwargs): @@ -433,14 +444,14 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF queryset=Prefix.objects.all(), widget=forms.MultipleHiddenInput() ) - site = forms.ModelChoiceField( + site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, widget=APISelect( api_url="/api/dcim/sites/" ) ) - vrf = forms.ModelChoiceField( + vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, label='VRF', @@ -453,7 +464,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF max_value=PREFIX_LENGTH_MAX, required=False ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( @@ -465,7 +476,7 @@ class PrefixBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF required=False, widget=StaticSelect2() ) - role = forms.ModelChoiceField( + role = DynamicModelChoiceField( queryset=Role.objects.all(), required=False, widget=APISelect( @@ -588,7 +599,15 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel queryset=Interface.objects.all(), required=False ) - nat_site = forms.ModelChoiceField( + vrf = DynamicModelChoiceField( + queryset=VRF.objects.all(), + required=False, + label='VRF', + widget=APISelect( + api_url="/api/ipam/vrfs/" + ) + ) + nat_site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, label='Site', @@ -664,9 +683,6 @@ class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldModel widgets = { 'status': StaticSelect2(), 'role': StaticSelect2(), - 'vrf': APISelect( - api_url="/api/ipam/vrfs/" - ) } def __init__(self, *args, **kwargs): @@ -741,6 +757,14 @@ class IPAddressBulkCreateForm(BootstrapMixin, forms.Form): class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): + vrf = DynamicModelChoiceField( + queryset=VRF.objects.all(), + required=False, + label='VRF', + widget=APISelect( + api_url="/api/ipam/vrfs/" + ) + ) class Meta: model = IPAddress @@ -750,9 +774,6 @@ class IPAddressBulkAddForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): widgets = { 'status': StaticSelect2(), 'role': StaticSelect2(), - 'vrf': APISelect( - api_url="/api/ipam/vrfs/" - ) } def __init__(self, *args, **kwargs): @@ -888,7 +909,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd queryset=IPAddress.objects.all(), widget=forms.MultipleHiddenInput() ) - vrf = forms.ModelChoiceField( + vrf = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, label='VRF', @@ -901,7 +922,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd max_value=IPADDRESS_MASK_LENGTH_MAX, required=False ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( @@ -934,7 +955,7 @@ class IPAddressBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEd class IPAddressAssignForm(BootstrapMixin, forms.Form): - vrf_id = forms.ModelChoiceField( + vrf_id = DynamicModelChoiceField( queryset=VRF.objects.all(), required=False, label='VRF', @@ -1014,6 +1035,13 @@ class IPAddressFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterFo # class VLANGroupForm(BootstrapMixin, forms.ModelForm): + site = DynamicModelChoiceField( + queryset=Site.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/sites/" + ) + ) slug = SlugField() class Meta: @@ -1021,11 +1049,6 @@ class VLANGroupForm(BootstrapMixin, forms.ModelForm): fields = [ 'site', 'name', 'slug', ] - widgets = { - 'site': APISelect( - api_url="/api/dcim/sites/" - ) - } class VLANGroupCSVForm(forms.ModelForm): @@ -1078,7 +1101,7 @@ class VLANGroupFilterForm(BootstrapMixin, forms.Form): # class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): - site = forms.ModelChoiceField( + site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, widget=APISelect( @@ -1098,6 +1121,13 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): api_url='/api/ipam/vlan-groups/', ) ) + role = DynamicModelChoiceField( + queryset=Role.objects.all(), + required=False, + widget=APISelect( + api_url="/api/ipam/roles/" + ) + ) tags = TagField(required=False) class Meta: @@ -1115,9 +1145,6 @@ class VLANForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): } widgets = { 'status': StaticSelect2(), - 'role': APISelect( - api_url="/api/ipam/roles/" - ) } @@ -1192,21 +1219,21 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor queryset=VLAN.objects.all(), widget=forms.MultipleHiddenInput() ) - site = forms.ModelChoiceField( + site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, widget=APISelect( api_url="/api/dcim/sites/" ) ) - group = forms.ModelChoiceField( + group = DynamicModelChoiceField( queryset=VLANGroup.objects.all(), required=False, widget=APISelect( api_url="/api/ipam/vlan-groups/" ) ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( @@ -1218,7 +1245,7 @@ class VLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditFor required=False, widget=StaticSelect2() ) - role = forms.ModelChoiceField( + role = DynamicModelChoiceField( queryset=Role.objects.all(), required=False, widget=APISelect( diff --git a/netbox/secrets/forms.py b/netbox/secrets/forms.py index 7554e6278..79064e0dd 100644 --- a/netbox/secrets/forms.py +++ b/netbox/secrets/forms.py @@ -8,8 +8,8 @@ from extras.forms import ( AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldFilterForm, CustomFieldModelForm, CustomFieldModelCSVForm, ) from utilities.forms import ( - APISelect, APISelectMultiple, BootstrapMixin, DynamicModelMultipleChoiceField, FlexibleModelChoiceField, SlugField, - StaticSelect2Multiple, TagFilterField + APISelect, APISelectMultiple, BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, + FlexibleModelChoiceField, SlugField, StaticSelect2Multiple, TagFilterField, ) from .constants import * from .models import Secret, SecretRole, UserKey @@ -87,6 +87,12 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm): label='Plaintext (verify)', widget=forms.PasswordInput() ) + role = DynamicModelChoiceField( + queryset=SecretRole.objects.all(), + widget=APISelect( + api_url="/api/secrets/secret-roles/" + ) + ) tags = TagField( required=False ) @@ -96,11 +102,6 @@ class SecretForm(BootstrapMixin, CustomFieldModelForm): fields = [ 'role', 'name', 'plaintext', 'plaintext2', 'tags', ] - widgets = { - 'role': APISelect( - api_url="/api/secrets/secret-roles/" - ) - } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -157,7 +158,7 @@ class SecretBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF queryset=Secret.objects.all(), widget=forms.MultipleHiddenInput() ) - role = forms.ModelChoiceField( + role = DynamicModelChoiceField( queryset=SecretRole.objects.all(), required=False, widget=APISelect( diff --git a/netbox/tenancy/forms.py b/netbox/tenancy/forms.py index f3379faa0..5b828b661 100644 --- a/netbox/tenancy/forms.py +++ b/netbox/tenancy/forms.py @@ -42,6 +42,13 @@ class TenantGroupCSVForm(forms.ModelForm): class TenantForm(BootstrapMixin, CustomFieldModelForm): slug = SlugField() + group = DynamicModelChoiceField( + queryset=TenantGroup.objects.all(), + required=False, + widget=APISelect( + api_url="/api/tenancy/tenant-groups/" + ) + ) comments = CommentField() tags = TagField( required=False @@ -49,14 +56,9 @@ class TenantForm(BootstrapMixin, CustomFieldModelForm): class Meta: model = Tenant - fields = [ + fields = ( 'name', 'slug', 'group', 'description', 'comments', 'tags', - ] - widgets = { - 'group': APISelect( - api_url="/api/tenancy/tenant-groups/" - ) - } + ) class TenantCSVForm(CustomFieldModelForm): @@ -85,7 +87,7 @@ class TenantBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEditF queryset=Tenant.objects.all(), widget=forms.MultipleHiddenInput() ) - group = forms.ModelChoiceField( + group = DynamicModelChoiceField( queryset=TenantGroup.objects.all(), required=False, widget=APISelect( diff --git a/netbox/utilities/forms.py b/netbox/utilities/forms.py index fc724224b..c9a857ad0 100644 --- a/netbox/utilities/forms.py +++ b/netbox/utilities/forms.py @@ -550,11 +550,7 @@ class TagFilterField(forms.MultipleChoiceField): super().__init__(label='Tags', choices=get_choices, required=False, *args, **kwargs) -class DynamicModelChoiceField(forms.ModelChoiceField): - """ - Override get_bound_field() to avoid pre-populating field choices with a SQL query. The field will be - rendered only with choices set via bound data. Choices are populated on-demand via the APISelect widget. - """ +class DynamicModelChoiceMixin: field_modifier = '' def get_bound_field(self, form, field_name): @@ -564,16 +560,24 @@ class DynamicModelChoiceField(forms.ModelChoiceField): # will be populated on-demand via the APISelect widget. field_name = '{}{}'.format(self.to_field_name or 'pk', self.field_modifier) if bound_field.data: - self.queryset = self.queryset.filter(**{field_name: bound_field.data}) + self.queryset = self.queryset.filter(**{field_name: self.prepare_value(bound_field.data)}) elif bound_field.initial: - self.queryset = self.queryset.filter(**{field_name: bound_field.initial}) + self.queryset = self.queryset.filter(**{field_name: self.prepare_value(bound_field.initial)}) else: self.queryset = self.queryset.none() return bound_field -class DynamicModelMultipleChoiceField(DynamicModelChoiceField): +class DynamicModelChoiceField(DynamicModelChoiceMixin, forms.ModelChoiceField): + """ + Override get_bound_field() to avoid pre-populating field choices with a SQL query. The field will be + rendered only with choices set via bound data. Choices are populated on-demand via the APISelect widget. + """ + pass + + +class DynamicModelMultipleChoiceField(DynamicModelChoiceMixin, forms.ModelMultipleChoiceField): """ A multiple-choice version of DynamicModelChoiceField. """ diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index 41b94a3c9..12393d400 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -76,6 +76,26 @@ class ClusterGroupCSVForm(forms.ModelForm): # class ClusterForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): + type = DynamicModelChoiceField( + queryset=ClusterType.objects.all(), + widget=APISelect( + api_url="/api/virtualization/cluster-types/" + ) + ) + group = DynamicModelChoiceField( + queryset=ClusterGroup.objects.all(), + required=False, + widget=APISelect( + api_url="/api/virtualization/cluster-groups/" + ) + ) + site = DynamicModelChoiceField( + queryset=Site.objects.all(), + required=False, + widget=APISelect( + api_url="/api/dcim/sites/" + ) + ) comments = CommentField() tags = TagField( required=False @@ -83,20 +103,9 @@ class ClusterForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): class Meta: model = Cluster - fields = [ + fields = ( 'name', 'type', 'group', 'tenant', 'site', 'comments', 'tags', - ] - widgets = { - 'type': APISelect( - api_url="/api/virtualization/cluster-types/" - ), - 'group': APISelect( - api_url="/api/virtualization/cluster-groups/" - ), - 'site': APISelect( - api_url="/api/dcim/sites/" - ), - } + ) class ClusterCSVForm(CustomFieldModelCSVForm): @@ -146,25 +155,28 @@ class ClusterBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldBulkEdit queryset=Cluster.objects.all(), widget=forms.MultipleHiddenInput() ) - type = forms.ModelChoiceField( + type = DynamicModelChoiceField( queryset=ClusterType.objects.all(), required=False, widget=APISelect( api_url="/api/virtualization/cluster-types/" ) ) - group = forms.ModelChoiceField( + group = DynamicModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=APISelect( api_url="/api/virtualization/cluster-groups/" ) ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), - required=False + required=False, + widget=APISelect( + api_url="/api/tenancy/tenants/" + ) ) - site = forms.ModelChoiceField( + site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, widget=APISelect( @@ -233,7 +245,7 @@ class ClusterFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFilterForm class ClusterAddDevicesForm(BootstrapMixin, forms.Form): - region = forms.ModelChoiceField( + region = DynamicModelChoiceField( queryset=Region.objects.all(), required=False, widget=APISelect( @@ -318,7 +330,7 @@ class ClusterRemoveDevicesForm(ConfirmationForm): # class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): - cluster_group = forms.ModelChoiceField( + cluster_group = DynamicModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=APISelect( @@ -337,6 +349,22 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): api_url='/api/virtualization/clusters/' ) ) + role = DynamicModelChoiceField( + queryset=DeviceRole.objects.all(), + widget=APISelect( + api_url="/api/dcim/device-roles/", + additional_query_params={ + "vm_role": "True" + } + ) + ) + platform = DynamicModelChoiceField( + queryset=Platform.objects.all(), + required=False, + widget=APISelect( + api_url='/api/dcim/platforms/' + ) + ) tags = TagField( required=False ) @@ -357,17 +385,8 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldModelForm): } widgets = { "status": StaticSelect2(), - "role": APISelect( - api_url="/api/dcim/device-roles/", - additional_query_params={ - "vm_role": "True" - } - ), 'primary_ip4': StaticSelect2(), 'primary_ip6': StaticSelect2(), - 'platform': APISelect( - api_url='/api/dcim/platforms/' - ) } def __init__(self, *args, **kwargs): @@ -477,14 +496,14 @@ class VirtualMachineBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldB initial='', widget=StaticSelect2(), ) - cluster = forms.ModelChoiceField( + cluster = DynamicModelChoiceField( queryset=Cluster.objects.all(), required=False, widget=APISelect( api_url='/api/virtualization/clusters/' ) ) - role = forms.ModelChoiceField( + role = DynamicModelChoiceField( queryset=DeviceRole.objects.filter( vm_role=True ), @@ -496,14 +515,14 @@ class VirtualMachineBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldB } ) ) - tenant = forms.ModelChoiceField( + tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False, widget=APISelect( api_url='/api/tenancy/tenants/' ) ) - platform = forms.ModelChoiceField( + platform = DynamicModelChoiceField( queryset=Platform.objects.all(), required=False, widget=APISelect( @@ -633,7 +652,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil # class InterfaceForm(BootstrapMixin, forms.ModelForm): - untagged_vlan = forms.ModelChoiceField( + untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelect( @@ -642,7 +661,7 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm): full=True ) ) - tagged_vlans = forms.ModelMultipleChoiceField( + tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple( @@ -759,7 +778,7 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form): required=False, widget=StaticSelect2(), ) - untagged_vlan = forms.ModelChoiceField( + untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelect( @@ -768,7 +787,7 @@ class InterfaceCreateForm(BootstrapMixin, forms.Form): full=True ) ) - tagged_vlans = forms.ModelMultipleChoiceField( + tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple( @@ -847,7 +866,7 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): required=False, widget=StaticSelect2() ) - untagged_vlan = forms.ModelChoiceField( + untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelect( @@ -856,7 +875,7 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): full=True ) ) - tagged_vlans = forms.ModelMultipleChoiceField( + tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple(