mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-15 19:52:52 -06:00
* Allow adding/removing tagged VLANs in bulk editing of Interfaces * Move vlan/interface-specific field operations to an overrideable method * Ensure interfaces are MODE_TAGGED before adding/removing tagged vlans * Add docstring for generic extra_object_field_operations * Move tagging ops into post_save_operations and use a TabbedGroup in the form
This commit is contained in:
parent
6035ad139a
commit
f873735dd4
@ -13,7 +13,7 @@ from tenancy.models import Tenant
|
||||
from users.models import User
|
||||
from utilities.forms import BulkEditForm, add_blank_choice, form_from_model
|
||||
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
|
||||
from utilities.forms.rendering import FieldSet, InlineFields
|
||||
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
|
||||
from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
|
||||
from wireless.models import WirelessLAN, WirelessLANGroup
|
||||
from wireless.choices import WirelessRoleChoices
|
||||
@ -1404,18 +1404,25 @@ class InterfaceBulkEditForm(
|
||||
parent = DynamicModelChoiceField(
|
||||
label=_('Parent'),
|
||||
queryset=Interface.objects.all(),
|
||||
required=False
|
||||
required=False,
|
||||
query_params={
|
||||
'virtual_chassis_member_id': '$device',
|
||||
}
|
||||
)
|
||||
bridge = DynamicModelChoiceField(
|
||||
label=_('Bridge'),
|
||||
queryset=Interface.objects.all(),
|
||||
required=False
|
||||
required=False,
|
||||
query_params={
|
||||
'virtual_chassis_member_id': '$device',
|
||||
}
|
||||
)
|
||||
lag = DynamicModelChoiceField(
|
||||
queryset=Interface.objects.all(),
|
||||
required=False,
|
||||
query_params={
|
||||
'type': 'lag',
|
||||
'virtual_chassis_member_id': '$device',
|
||||
},
|
||||
label=_('LAG')
|
||||
)
|
||||
@ -1472,6 +1479,7 @@ class InterfaceBulkEditForm(
|
||||
required=False,
|
||||
query_params={
|
||||
'group_id': '$vlan_group',
|
||||
'available_on_device': '$device',
|
||||
},
|
||||
label=_('Untagged VLAN')
|
||||
)
|
||||
@ -1480,9 +1488,28 @@ class InterfaceBulkEditForm(
|
||||
required=False,
|
||||
query_params={
|
||||
'group_id': '$vlan_group',
|
||||
'available_on_device': '$device',
|
||||
},
|
||||
label=_('Tagged VLANs')
|
||||
)
|
||||
add_tagged_vlans = DynamicModelMultipleChoiceField(
|
||||
label=_('Add tagged VLANs'),
|
||||
queryset=VLAN.objects.all(),
|
||||
required=False,
|
||||
query_params={
|
||||
'group_id': '$vlan_group',
|
||||
'available_on_device': '$device',
|
||||
},
|
||||
)
|
||||
remove_tagged_vlans = DynamicModelMultipleChoiceField(
|
||||
label=_('Remove tagged VLANs'),
|
||||
queryset=VLAN.objects.all(),
|
||||
required=False,
|
||||
query_params={
|
||||
'group_id': '$vlan_group',
|
||||
'available_on_device': '$device',
|
||||
}
|
||||
)
|
||||
vrf = DynamicModelChoiceField(
|
||||
queryset=VRF.objects.all(),
|
||||
required=False,
|
||||
@ -1509,7 +1536,13 @@ class InterfaceBulkEditForm(
|
||||
FieldSet('vdcs', 'mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected', name=_('Operation')),
|
||||
FieldSet('poe_mode', 'poe_type', name=_('PoE')),
|
||||
FieldSet('parent', 'bridge', 'lag', name=_('Related Interfaces')),
|
||||
FieldSet('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans', name=_('802.1Q Switching')),
|
||||
FieldSet('mode', 'vlan_group', 'untagged_vlan', name=_('802.1Q Switching')),
|
||||
FieldSet(
|
||||
TabbedGroups(
|
||||
FieldSet('tagged_vlans', name=_('Assignment')),
|
||||
FieldSet('add_tagged_vlans', 'remove_tagged_vlans', name=_('Add/Remove')),
|
||||
),
|
||||
),
|
||||
FieldSet(
|
||||
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans',
|
||||
name=_('Wireless')
|
||||
@ -1523,19 +1556,7 @@ class InterfaceBulkEditForm(
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.device_id:
|
||||
device = Device.objects.filter(pk=self.device_id).first()
|
||||
|
||||
# Restrict parent/bridge/LAG interface assignment by device
|
||||
self.fields['parent'].widget.add_query_param('virtual_chassis_member_id', device.pk)
|
||||
self.fields['bridge'].widget.add_query_param('virtual_chassis_member_id', device.pk)
|
||||
self.fields['lag'].widget.add_query_param('virtual_chassis_member_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:
|
||||
if not self.device_id:
|
||||
# See #4523
|
||||
if 'pk' in self.initial:
|
||||
site = None
|
||||
@ -1559,6 +1580,13 @@ class InterfaceBulkEditForm(
|
||||
'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE]
|
||||
)
|
||||
|
||||
self.fields['add_tagged_vlans'].widget.add_query_param(
|
||||
'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE]
|
||||
)
|
||||
self.fields['remove_tagged_vlans'].widget.add_query_param(
|
||||
'site_id', [site.pk, settings.FILTERS_NULL_CHOICE_VALUE]
|
||||
)
|
||||
|
||||
self.fields['parent'].choices = ()
|
||||
self.fields['parent'].widget.attrs['disabled'] = True
|
||||
self.fields['bridge'].choices = ()
|
||||
|
@ -35,7 +35,7 @@ from virtualization.forms import VirtualMachineFilterForm
|
||||
from virtualization.models import VirtualMachine
|
||||
from virtualization.tables import VirtualMachineTable
|
||||
from . import filtersets, forms, tables
|
||||
from .choices import DeviceFaceChoices
|
||||
from .choices import DeviceFaceChoices, InterfaceModeChoices
|
||||
from .models import *
|
||||
|
||||
CABLE_TERMINATION_TYPES = {
|
||||
@ -2616,6 +2616,16 @@ class InterfaceBulkEditView(generic.BulkEditView):
|
||||
table = tables.InterfaceTable
|
||||
form = forms.InterfaceBulkEditForm
|
||||
|
||||
def post_save_operations(self, form, obj):
|
||||
super().post_save_operations(form, obj)
|
||||
|
||||
# Add/remove tagged VLANs
|
||||
if obj.mode == InterfaceModeChoices.MODE_TAGGED:
|
||||
if form.cleaned_data.get('add_tagged_vlans', None):
|
||||
obj.tagged_vlans.add(*form.cleaned_data['add_tagged_vlans'])
|
||||
if form.cleaned_data.get('remove_tagged_vlans', None):
|
||||
obj.tagged_vlans.remove(*form.cleaned_data['remove_tagged_vlans'])
|
||||
|
||||
|
||||
class InterfaceBulkRenameView(generic.BulkRenameView):
|
||||
queryset = Interface.objects.all()
|
||||
|
@ -541,6 +541,17 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
def get_required_permission(self):
|
||||
return get_permission_for_model(self.queryset.model, 'change')
|
||||
|
||||
def post_save_operations(self, form, obj):
|
||||
"""
|
||||
This method is called for each object in _update_objects. Override to perform additional object-level
|
||||
operations that are specific to a particular ModelForm.
|
||||
"""
|
||||
# Add/remove tags
|
||||
if form.cleaned_data.get('add_tags', None):
|
||||
obj.tags.add(*form.cleaned_data['add_tags'])
|
||||
if form.cleaned_data.get('remove_tags', None):
|
||||
obj.tags.remove(*form.cleaned_data['remove_tags'])
|
||||
|
||||
def _update_objects(self, form, request):
|
||||
custom_fields = getattr(form, 'custom_fields', {})
|
||||
standard_fields = [
|
||||
@ -612,11 +623,7 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView):
|
||||
elif form.cleaned_data[name]:
|
||||
getattr(obj, name).set(form.cleaned_data[name])
|
||||
|
||||
# Add/remove tags
|
||||
if form.cleaned_data.get('add_tags', None):
|
||||
obj.tags.add(*form.cleaned_data['add_tags'])
|
||||
if form.cleaned_data.get('remove_tags', None):
|
||||
obj.tags.remove(*form.cleaned_data['remove_tags'])
|
||||
self.post_save_operations(form, obj)
|
||||
|
||||
# Rebuild the tree for MPTT models
|
||||
if issubclass(self.queryset.model, MPTTModel):
|
||||
|
Loading…
Reference in New Issue
Block a user