From 1ad6d94dc3412eaa54a82ff6d6a0c46adc6d3ba0 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 26 Sep 2023 13:09:20 -0400 Subject: [PATCH] Fixes #13843: Fix assignment of VLAN group scope during bulk edit (#13887) * Update VLANGroup bulk edit form to support all scope types * Fixes #13843: Fix scope assignment for VLAN groups during bulk edit * Add missed static file * Restore graphiql static assets --- netbox/ipam/forms/bulk_edit.py | 85 ++++++++++++++++-- netbox/netbox/views/generic/bulk_views.py | 5 +- netbox/project-static/dist/netbox.js | Bin 529836 -> 529868 bytes netbox/project-static/dist/netbox.js.map | Bin 450242 -> 450255 bytes .../project-static/src/forms/scopeSelector.ts | 1 + 5 files changed, 82 insertions(+), 9 deletions(-) diff --git a/netbox/ipam/forms/bulk_edit.py b/netbox/ipam/forms/bulk_edit.py index 548d01afa..f0a8286fc 100644 --- a/netbox/ipam/forms/bulk_edit.py +++ b/netbox/ipam/forms/bulk_edit.py @@ -1,7 +1,8 @@ from django import forms +from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ -from dcim.models import Region, Site, SiteGroup +from dcim.models import Location, Rack, Region, Site, SiteGroup from ipam.choices import * from ipam.constants import * from ipam.models import * @@ -10,9 +11,10 @@ from netbox.forms import NetBoxModelBulkEditForm from tenancy.models import Tenant from utilities.forms import add_blank_choice from utilities.forms.fields import ( - CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField, + CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, NumericArrayField, ) from utilities.forms.widgets import BulkEditNullBooleanSelect +from virtualization.models import Cluster, ClusterGroup __all__ = ( 'AggregateBulkEditForm', @@ -407,11 +409,6 @@ class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm): class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): - site = DynamicModelChoiceField( - label=_('Site'), - queryset=Site.objects.all(), - required=False - ) min_vid = forms.IntegerField( min_value=VLAN_VID_MIN, max_value=VLAN_VID_MAX, @@ -429,12 +426,84 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm): max_length=200, required=False ) + scope_type = ContentTypeChoiceField( + label=_('Scope type'), + queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES), + required=False + ) + scope_id = forms.IntegerField( + required=False, + widget=forms.HiddenInput() + ) + region = DynamicModelChoiceField( + label=_('Region'), + queryset=Region.objects.all(), + required=False + ) + sitegroup = DynamicModelChoiceField( + queryset=SiteGroup.objects.all(), + required=False, + label=_('Site group') + ) + site = DynamicModelChoiceField( + label=_('Site'), + queryset=Site.objects.all(), + required=False, + query_params={ + 'region_id': '$region', + 'group_id': '$sitegroup', + } + ) + location = DynamicModelChoiceField( + label=_('Location'), + queryset=Location.objects.all(), + required=False, + query_params={ + 'site_id': '$site', + } + ) + rack = DynamicModelChoiceField( + label=_('Rack'), + queryset=Rack.objects.all(), + required=False, + query_params={ + 'site_id': '$site', + 'location_id': '$location', + } + ) + clustergroup = DynamicModelChoiceField( + queryset=ClusterGroup.objects.all(), + required=False, + label=_('Cluster group') + ) + cluster = DynamicModelChoiceField( + label=_('Cluster'), + queryset=Cluster.objects.all(), + required=False, + query_params={ + 'group_id': '$clustergroup', + } + ) model = VLANGroup fieldsets = ( (None, ('site', 'min_vid', 'max_vid', 'description')), + (_('Scope'), ('scope_type', 'region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster')), ) - nullable_fields = ('site', 'description') + nullable_fields = ('description',) + + def clean(self): + super().clean() + + # Assign scope based on scope_type + if self.cleaned_data.get('scope_type'): + scope_field = self.cleaned_data['scope_type'].model + if scope_obj := self.cleaned_data.get(scope_field): + self.cleaned_data['scope_id'] = scope_obj.pk + self.changed_data.append('scope_id') + else: + self.cleaned_data.pop('scope_type') + self.changed_data.remove('scope_type') class VLANBulkEditForm(NetBoxModelBulkEditForm): diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index bef524bce..676e3f5af 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -3,6 +3,7 @@ import re from copy import deepcopy from django.contrib import messages +from django.contrib.contenttypes.fields import GenericRel from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError from django.db import transaction, IntegrityError @@ -519,9 +520,11 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView): model_field = self.queryset.model._meta.get_field(name) if isinstance(model_field, (ManyToManyField, ManyToManyRel)): m2m_fields[name] = model_field + elif isinstance(model_field, GenericRel): + # Ignore generic relations (these may be used for other purposes in the form) + continue else: model_fields[name] = model_field - except FieldDoesNotExist: # This form field is used to modify a field rather than set its value directly model_fields[name] = None diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index ac5a6800292bbdffd0407da2b0b99f25e226bfb2..7d16f9916551ad8f5deea05dcc5db33d79d507df 100644 GIT binary patch delta 51 zcmZ3pTH(xUg@zW!7N!>F7M2#)Eo>J!`E|;267$lF@=FWilS*^4+xK&_0Wtgb{hSF7M2#)Eo>J!+dpu!0Wtgb51br#xB>cf4RHVf diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 7dbfeca7a9d3a26ecee4ea6b390076a4186e29d3..1797f57ceb117de051fd4be372f96b16e715f26f 100644 GIT binary patch delta 84 zcmX@KSNi;3>4p}@7N!>F7M3lnmyGoy9i20rbn=}X9Sfaw5*-7}oOB``9n+n4+#MZL mK}4mKj4p}@7N!>F7M3lnmy8t?9RqWmbRr!c)17tP9UW6aM5U9CvtyQfg{zLg YWA-)^R!JttjCLtA*6mVeY+F|V084uo6#xJL diff --git a/netbox/project-static/src/forms/scopeSelector.ts b/netbox/project-static/src/forms/scopeSelector.ts index 14ef972f8..f7b77f041 100644 --- a/netbox/project-static/src/forms/scopeSelector.ts +++ b/netbox/project-static/src/forms/scopeSelector.ts @@ -88,6 +88,7 @@ const showHideLayout: ShowHideLayout = { const showHideMap: ShowHideMap = { vlangroup_add: 'vlangroup', vlangroup_edit: 'vlangroup', + vlangroup_bulk_edit: 'vlangroup', }; /**