Răsfoiți Sursa

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
Jeremy Stretch 2 ani în urmă
părinte
comite
1ad6d94dc3

+ 77 - 8
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):

+ 4 - 1
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

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
netbox/project-static/dist/netbox.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
netbox/project-static/dist/netbox.js.map


+ 1 - 0
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',
 };
 
 /**

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff