from django import forms from django.utils.translation import gettext as _ from django.contrib.auth.models import User from timezone_field import TimeZoneFormField from dcim.choices import * from dcim.constants import * from dcim.models import * from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm from ipam.constants import BGP_ASN_MIN, BGP_ASN_MAX from ipam.models import VLAN, ASN from tenancy.models import Tenant from utilities.forms import ( add_blank_choice, BulkEditForm, BulkEditNullBooleanSelect, ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, form_from_model, SmallTextarea, StaticSelect, ) __all__ = ( 'CableBulkEditForm', 'ConsolePortBulkEditForm', 'ConsolePortTemplateBulkEditForm', 'ConsoleServerPortBulkEditForm', 'ConsoleServerPortTemplateBulkEditForm', 'DeviceBayBulkEditForm', 'DeviceBayTemplateBulkEditForm', 'DeviceBulkEditForm', 'DeviceRoleBulkEditForm', 'DeviceTypeBulkEditForm', 'FrontPortBulkEditForm', 'FrontPortTemplateBulkEditForm', 'InterfaceBulkEditForm', 'InterfaceTemplateBulkEditForm', 'InventoryItemBulkEditForm', 'LocationBulkEditForm', 'ManufacturerBulkEditForm', 'PlatformBulkEditForm', 'PowerFeedBulkEditForm', 'PowerOutletBulkEditForm', 'PowerOutletTemplateBulkEditForm', 'PowerPanelBulkEditForm', 'PowerPortBulkEditForm', 'PowerPortTemplateBulkEditForm', 'RackBulkEditForm', 'RackReservationBulkEditForm', 'RackRoleBulkEditForm', 'RearPortBulkEditForm', 'RearPortTemplateBulkEditForm', 'RegionBulkEditForm', 'SiteBulkEditForm', 'SiteGroupBulkEditForm', 'VirtualChassisBulkEditForm', ) class RegionBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Region.objects.all(), widget=forms.MultipleHiddenInput ) parent = DynamicModelChoiceField( queryset=Region.objects.all(), required=False ) description = forms.CharField( max_length=200, required=False ) class Meta: nullable_fields = ['parent', 'description'] class SiteGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=SiteGroup.objects.all(), widget=forms.MultipleHiddenInput ) parent = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False ) description = forms.CharField( max_length=200, required=False ) class Meta: nullable_fields = ['parent', 'description'] class SiteBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Site.objects.all(), widget=forms.MultipleHiddenInput ) status = forms.ChoiceField( choices=add_blank_choice(SiteStatusChoices), required=False, initial='', widget=StaticSelect() ) region = DynamicModelChoiceField( queryset=Region.objects.all(), required=False ) group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False ) tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False ) asn = forms.IntegerField( min_value=BGP_ASN_MIN, max_value=BGP_ASN_MAX, required=False, label='ASN' ) asns = DynamicModelMultipleChoiceField( queryset=ASN.objects.all(), label=_('ASNs'), required=False ) description = forms.CharField( max_length=100, required=False ) time_zone = TimeZoneFormField( choices=add_blank_choice(TimeZoneFormField().choices), required=False, widget=StaticSelect() ) class Meta: nullable_fields = [ 'region', 'group', 'tenant', 'asn', 'asns', 'description', 'time_zone', ] class LocationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Location.objects.all(), widget=forms.MultipleHiddenInput ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False ) parent = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False ) description = forms.CharField( max_length=200, required=False ) class Meta: nullable_fields = ['parent', 'tenant', 'description'] class RackRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=RackRole.objects.all(), widget=forms.MultipleHiddenInput ) color = ColorField( required=False ) description = forms.CharField( max_length=200, required=False ) class Meta: nullable_fields = ['color', 'description'] class RackBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Rack.objects.all(), widget=forms.MultipleHiddenInput ) region = DynamicModelChoiceField( queryset=Region.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site_group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False ) status = forms.ChoiceField( choices=add_blank_choice(RackStatusChoices), required=False, initial='', widget=StaticSelect() ) role = DynamicModelChoiceField( queryset=RackRole.objects.all(), required=False ) serial = forms.CharField( max_length=50, required=False, label='Serial Number' ) asset_tag = forms.CharField( max_length=50, required=False ) type = forms.ChoiceField( choices=add_blank_choice(RackTypeChoices), required=False, widget=StaticSelect() ) width = forms.ChoiceField( choices=add_blank_choice(RackWidthChoices), required=False, widget=StaticSelect() ) u_height = forms.IntegerField( required=False, label='Height (U)' ) desc_units = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label='Descending units' ) outer_width = forms.IntegerField( required=False, min_value=1 ) outer_depth = forms.IntegerField( required=False, min_value=1 ) outer_unit = forms.ChoiceField( choices=add_blank_choice(RackDimensionUnitChoices), required=False, widget=StaticSelect() ) comments = CommentField( widget=SmallTextarea, label='Comments' ) class Meta: nullable_fields = [ 'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'comments', ] class RackReservationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=RackReservation.objects.all(), widget=forms.MultipleHiddenInput() ) user = forms.ModelChoiceField( queryset=User.objects.order_by( 'username' ), required=False, widget=StaticSelect() ) tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False ) description = forms.CharField( max_length=100, required=False ) class Meta: nullable_fields = [] class ManufacturerBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Manufacturer.objects.all(), widget=forms.MultipleHiddenInput ) description = forms.CharField( max_length=200, required=False ) class Meta: nullable_fields = ['description'] class DeviceTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=DeviceType.objects.all(), widget=forms.MultipleHiddenInput() ) manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False ) u_height = forms.IntegerField( min_value=1, required=False ) is_full_depth = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect(), label='Is full depth' ) airflow = forms.ChoiceField( choices=add_blank_choice(DeviceAirflowChoices), required=False, widget=StaticSelect() ) class Meta: nullable_fields = ['airflow'] class DeviceRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=DeviceRole.objects.all(), widget=forms.MultipleHiddenInput ) color = ColorField( required=False ) vm_role = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label='VM role' ) description = forms.CharField( max_length=200, required=False ) class Meta: nullable_fields = ['color', 'description'] class PlatformBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Platform.objects.all(), widget=forms.MultipleHiddenInput ) manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False ) napalm_driver = forms.CharField( max_length=50, required=False ) # TODO: Bulk edit support for napalm_args description = forms.CharField( max_length=200, required=False ) class Meta: nullable_fields = ['manufacturer', 'napalm_driver', 'description'] class DeviceBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Device.objects.all(), widget=forms.MultipleHiddenInput() ) manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False ) device_type = DynamicModelChoiceField( queryset=DeviceType.objects.all(), required=False, query_params={ 'manufacturer_id': '$manufacturer' } ) device_role = DynamicModelChoiceField( queryset=DeviceRole.objects.all(), required=False ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False ) platform = DynamicModelChoiceField( queryset=Platform.objects.all(), required=False ) status = forms.ChoiceField( choices=add_blank_choice(DeviceStatusChoices), required=False, widget=StaticSelect() ) airflow = forms.ChoiceField( choices=add_blank_choice(DeviceAirflowChoices), required=False, widget=StaticSelect() ) serial = forms.CharField( max_length=50, required=False, label='Serial Number' ) class Meta: nullable_fields = [ 'tenant', 'platform', 'serial', 'airflow', ] class CableBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=Cable.objects.all(), widget=forms.MultipleHiddenInput ) type = forms.ChoiceField( choices=add_blank_choice(CableTypeChoices), required=False, initial='', widget=StaticSelect() ) status = forms.ChoiceField( choices=add_blank_choice(LinkStatusChoices), required=False, widget=StaticSelect(), initial='' ) tenant = DynamicModelChoiceField( queryset=Tenant.objects.all(), required=False ) label = forms.CharField( max_length=100, required=False ) color = ColorField( required=False ) length = forms.DecimalField( min_value=0, required=False ) length_unit = forms.ChoiceField( choices=add_blank_choice(CableLengthUnitChoices), required=False, initial='', widget=StaticSelect() ) class Meta: nullable_fields = [ 'type', 'status', 'tenant', 'label', 'color', 'length', ] def clean(self): super().clean() # Validate length/unit length = self.cleaned_data.get('length') length_unit = self.cleaned_data.get('length_unit') if length and not length_unit: raise forms.ValidationError({ 'length_unit': "Must specify a unit when setting length" }) class VirtualChassisBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=VirtualChassis.objects.all(), widget=forms.MultipleHiddenInput() ) domain = forms.CharField( max_length=30, required=False ) class Meta: nullable_fields = ['domain'] class PowerPanelBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=PowerPanel.objects.all(), widget=forms.MultipleHiddenInput ) region = DynamicModelChoiceField( queryset=Region.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site_group = DynamicModelChoiceField( queryset=SiteGroup.objects.all(), required=False, initial_params={ 'sites': '$site' } ) site = DynamicModelChoiceField( queryset=Site.objects.all(), required=False, query_params={ 'region_id': '$region', 'group_id': '$site_group', } ) location = DynamicModelChoiceField( queryset=Location.objects.all(), required=False, query_params={ 'site_id': '$site' } ) class Meta: nullable_fields = ['location'] class PowerFeedBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=PowerFeed.objects.all(), widget=forms.MultipleHiddenInput ) power_panel = DynamicModelChoiceField( queryset=PowerPanel.objects.all(), required=False ) rack = DynamicModelChoiceField( queryset=Rack.objects.all(), required=False, ) status = forms.ChoiceField( choices=add_blank_choice(PowerFeedStatusChoices), required=False, initial='', widget=StaticSelect() ) type = forms.ChoiceField( choices=add_blank_choice(PowerFeedTypeChoices), required=False, initial='', widget=StaticSelect() ) supply = forms.ChoiceField( choices=add_blank_choice(PowerFeedSupplyChoices), required=False, initial='', widget=StaticSelect() ) phase = forms.ChoiceField( choices=add_blank_choice(PowerFeedPhaseChoices), required=False, initial='', widget=StaticSelect() ) voltage = forms.IntegerField( required=False ) amperage = forms.IntegerField( required=False ) max_utilization = forms.IntegerField( required=False ) mark_connected = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect ) comments = CommentField( widget=SmallTextarea, label='Comments' ) class Meta: nullable_fields = [ 'location', 'comments', ] # # Device component templates # class ConsolePortTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=ConsolePortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( max_length=64, required=False ) type = forms.ChoiceField( choices=add_blank_choice(ConsolePortTypeChoices), required=False, widget=StaticSelect() ) class Meta: nullable_fields = ('label', 'type', 'description') class ConsoleServerPortTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=ConsoleServerPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( max_length=64, required=False ) type = forms.ChoiceField( choices=add_blank_choice(ConsolePortTypeChoices), required=False, widget=StaticSelect() ) description = forms.CharField( required=False ) class Meta: nullable_fields = ('label', 'type', 'description') class PowerPortTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=PowerPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( max_length=64, required=False ) type = forms.ChoiceField( choices=add_blank_choice(PowerPortTypeChoices), required=False, widget=StaticSelect() ) maximum_draw = forms.IntegerField( min_value=1, required=False, help_text="Maximum power draw (watts)" ) allocated_draw = forms.IntegerField( min_value=1, required=False, help_text="Allocated power draw (watts)" ) description = forms.CharField( required=False ) class Meta: nullable_fields = ('label', 'type', 'maximum_draw', 'allocated_draw', 'description') class PowerOutletTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=PowerOutletTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) device_type = forms.ModelChoiceField( queryset=DeviceType.objects.all(), required=False, disabled=True, widget=forms.HiddenInput() ) label = forms.CharField( max_length=64, required=False ) type = forms.ChoiceField( choices=add_blank_choice(PowerOutletTypeChoices), required=False, widget=StaticSelect() ) power_port = forms.ModelChoiceField( queryset=PowerPortTemplate.objects.all(), required=False ) feed_leg = forms.ChoiceField( choices=add_blank_choice(PowerOutletFeedLegChoices), required=False, widget=StaticSelect() ) description = forms.CharField( required=False ) class Meta: nullable_fields = ('label', 'type', 'power_port', 'feed_leg', 'description') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit power_port queryset to PowerPortTemplates which belong to the parent DeviceType if 'device_type' in self.initial: device_type = DeviceType.objects.filter(pk=self.initial['device_type']).first() self.fields['power_port'].queryset = PowerPortTemplate.objects.filter(device_type=device_type) else: self.fields['power_port'].choices = () self.fields['power_port'].widget.attrs['disabled'] = True class InterfaceTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=InterfaceTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( max_length=64, required=False ) type = forms.ChoiceField( choices=add_blank_choice(InterfaceTypeChoices), required=False, widget=StaticSelect() ) mgmt_only = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label='Management only' ) description = forms.CharField( required=False ) class Meta: nullable_fields = ('label', 'description') class FrontPortTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=FrontPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( max_length=64, required=False ) type = forms.ChoiceField( choices=add_blank_choice(PortTypeChoices), required=False, widget=StaticSelect() ) color = ColorField( required=False ) description = forms.CharField( required=False ) class Meta: nullable_fields = ('description',) class RearPortTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=RearPortTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( max_length=64, required=False ) type = forms.ChoiceField( choices=add_blank_choice(PortTypeChoices), required=False, widget=StaticSelect() ) color = ColorField( required=False ) description = forms.CharField( required=False ) class Meta: nullable_fields = ('description',) class DeviceBayTemplateBulkEditForm(BulkEditForm): pk = forms.ModelMultipleChoiceField( queryset=DeviceBayTemplate.objects.all(), widget=forms.MultipleHiddenInput() ) label = forms.CharField( max_length=64, required=False ) description = forms.CharField( required=False ) class Meta: nullable_fields = ('label', 'description') # # Device components # class ConsolePortBulkEditForm( form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=ConsolePort.objects.all(), widget=forms.MultipleHiddenInput() ) mark_connected = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect ) class Meta: nullable_fields = ['label', 'description'] class ConsoleServerPortBulkEditForm( form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'mark_connected', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=ConsoleServerPort.objects.all(), widget=forms.MultipleHiddenInput() ) mark_connected = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect ) class Meta: nullable_fields = ['label', 'description'] class PowerPortBulkEditForm( form_from_model(PowerPort, ['label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=PowerPort.objects.all(), widget=forms.MultipleHiddenInput() ) mark_connected = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect ) class Meta: nullable_fields = ['label', 'description'] class PowerOutletBulkEditForm( form_from_model(PowerOutlet, ['label', 'type', 'feed_leg', 'power_port', 'mark_connected', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=PowerOutlet.objects.all(), widget=forms.MultipleHiddenInput() ) device = forms.ModelChoiceField( queryset=Device.objects.all(), required=False, disabled=True, widget=forms.HiddenInput() ) mark_connected = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect ) class Meta: nullable_fields = ['label', 'type', 'feed_leg', 'power_port', 'description'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit power_port queryset to PowerPorts which belong to the parent Device if 'device' in self.initial: device = Device.objects.filter(pk=self.initial['device']).first() self.fields['power_port'].queryset = PowerPort.objects.filter(device=device) else: self.fields['power_port'].choices = () self.fields['power_port'].widget.attrs['disabled'] = True class InterfaceBulkEditForm( form_from_model(Interface, [ 'label', 'type', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', ]), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput() ) device = forms.ModelChoiceField( queryset=Device.objects.all(), required=False, disabled=True, widget=forms.HiddenInput() ) enabled = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect ) parent = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False ) bridge = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False ) lag = DynamicModelChoiceField( queryset=Interface.objects.all(), required=False, query_params={ 'type': 'lag', } ) mgmt_only = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect, label='Management only' ) mark_connected = forms.NullBooleanField( required=False, widget=BulkEditNullBooleanSelect ) untagged_vlan = DynamicModelChoiceField( queryset=VLAN.objects.all(), required=False ) tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False ) class Meta: nullable_fields = [ 'label', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', ] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'device' in self.initial: device = Device.objects.filter(pk=self.initial['device']).first() # Restrict parent/bridge/LAG interface assignment by device self.fields['parent'].widget.add_query_param('device_id', device.pk) self.fields['bridge'].widget.add_query_param('device_id', device.pk) self.fields['lag'].widget.add_query_param('device_id', device.pk) # Limit VLAN choices by device self.fields['untagged_vlan'].widget.add_query_param('available_on_device', device.pk) self.fields['tagged_vlans'].widget.add_query_param('available_on_device', device.pk) else: # See #4523 if 'pk' in self.initial: site = None interfaces = Interface.objects.filter(pk__in=self.initial['pk']).prefetch_related('device__site') # Check interface sites. First interface should set site, further interfaces will either continue the # loop or reset back to no site and break the loop. for interface in interfaces: if site is None: site = interface.device.site elif interface.device.site is not site: site = None break if site is not None: self.fields['untagged_vlan'].widget.add_query_param('site_id', site.pk) self.fields['tagged_vlans'].widget.add_query_param('site_id', site.pk) self.fields['parent'].choices = () self.fields['parent'].widget.attrs['disabled'] = True self.fields['bridge'].choices = () self.fields['bridge'].widget.attrs['disabled'] = True self.fields['lag'].choices = () self.fields['lag'].widget.attrs['disabled'] = True def clean(self): super().clean() # Untagged interfaces cannot be assigned tagged VLANs if self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and self.cleaned_data['tagged_vlans']: raise forms.ValidationError({ 'mode': "An access interface cannot have tagged VLANs assigned." }) # Remove all tagged VLAN assignments from "tagged all" interfaces elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL: self.cleaned_data['tagged_vlans'] = [] class FrontPortBulkEditForm( form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=FrontPort.objects.all(), widget=forms.MultipleHiddenInput() ) class Meta: nullable_fields = ['label', 'description'] class RearPortBulkEditForm( form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=RearPort.objects.all(), widget=forms.MultipleHiddenInput() ) class Meta: nullable_fields = ['label', 'description'] class DeviceBayBulkEditForm( form_from_model(DeviceBay, ['label', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=DeviceBay.objects.all(), widget=forms.MultipleHiddenInput() ) class Meta: nullable_fields = ['label', 'description'] class InventoryItemBulkEditForm( form_from_model(InventoryItem, ['label', 'manufacturer', 'part_id', 'description']), AddRemoveTagsForm, CustomFieldModelBulkEditForm ): pk = forms.ModelMultipleChoiceField( queryset=InventoryItem.objects.all(), widget=forms.MultipleHiddenInput() ) manufacturer = DynamicModelChoiceField( queryset=Manufacturer.objects.all(), required=False ) class Meta: nullable_fields = ['label', 'manufacturer', 'part_id', 'description']