Add changelog message support to BulkRenameView for models that support change logging. Introduce NetBoxModelBulkRenameForm as a changelog-aware wrapper around the existing utilities.forms.BulkRenameForm, preserving the existing import path while avoiding circular imports. Set _changelog_message before saving renamed objects in both MPTT and non-MPTT rename paths, and add a regression test to verify that the submitted message is recorded on the resulting ObjectChange records. Remove unused VMInterfaceBulkRenameForm and VirtualDiskBulkRenameForm, along with their unused view references, since BulkRenameView constructs its form dynamically.
This commit is contained in:
parent
f2187ceb8f
commit
c20e6dd2ee
|
|
@ -1,5 +1,6 @@
|
|||
from .bulk_edit import *
|
||||
from .bulk_import import *
|
||||
from .bulk_rename import *
|
||||
from .filtersets import *
|
||||
from .model_forms import *
|
||||
from .search import *
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
from utilities.forms import BulkRenameForm
|
||||
|
||||
from .mixins import ChangelogMessageMixin
|
||||
|
||||
__all__ = (
|
||||
'NetBoxModelBulkRenameForm',
|
||||
)
|
||||
|
||||
|
||||
class NetBoxModelBulkRenameForm(ChangelogMessageMixin, BulkRenameForm):
|
||||
"""
|
||||
Extends BulkRenameForm with a changelog message field for NetBox models that support change logging.
|
||||
"""
|
||||
pass
|
||||
|
|
@ -22,6 +22,7 @@ from core.models import ObjectType
|
|||
from core.signals import clear_events
|
||||
from extras.choices import CustomFieldUIEditableChoices
|
||||
from extras.models import CustomField, ExportTemplate
|
||||
from netbox.forms.bulk_rename import NetBoxModelBulkRenameForm
|
||||
from netbox.models.features import ChangeLoggingMixin
|
||||
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
|
||||
from utilities.error_handlers import handle_protectederror
|
||||
|
|
@ -881,8 +882,14 @@ class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Create a new Form class from BulkRenameForm
|
||||
class _Form(BulkRenameForm):
|
||||
# Use the changelog-aware form for models that support change logging
|
||||
base_form = (
|
||||
NetBoxModelBulkRenameForm
|
||||
if issubclass(self.queryset.model, ChangeLoggingMixin)
|
||||
else BulkRenameForm
|
||||
)
|
||||
|
||||
class _Form(base_form):
|
||||
pk = ModelMultipleChoiceField(
|
||||
queryset=self.queryset,
|
||||
widget=MultipleHiddenInput()
|
||||
|
|
@ -940,10 +947,12 @@ class BulkRenameView(GetReturnURLMixin, BaseMultiObjectView):
|
|||
with self.queryset.model.objects.delay_mptt_updates():
|
||||
for obj in selected_objects:
|
||||
setattr(obj, self.field_name, obj.new_name)
|
||||
obj._changelog_message = form.cleaned_data.get('changelog_message', '')
|
||||
obj.save()
|
||||
else:
|
||||
for obj in selected_objects:
|
||||
setattr(obj, self.field_name, obj.new_name)
|
||||
obj._changelog_message = form.cleaned_data.get('changelog_message', '')
|
||||
obj.save()
|
||||
|
||||
# Enforce constrained permissions
|
||||
|
|
|
|||
|
|
@ -1049,6 +1049,41 @@ class ViewTestCases:
|
|||
for i, instance in enumerate(self._get_queryset().filter(pk__in=pk_list)):
|
||||
self.assertEqual(instance.name, f'{objects[i].name}X')
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_bulk_rename_objects_with_changelog_message(self):
|
||||
if not issubclass(self.model, ChangeLoggingMixin):
|
||||
self.skipTest("Model does not support change logging")
|
||||
objects = self._get_queryset().all()[:3]
|
||||
pk_list = [obj.pk for obj in objects]
|
||||
data = {
|
||||
'pk': pk_list,
|
||||
'_apply': True,
|
||||
'changelog_message': 'Bulk rename test message',
|
||||
}
|
||||
data.update(self.rename_data)
|
||||
|
||||
# Assign model-level permission
|
||||
obj_perm = ObjectPermission(
|
||||
name='Test permission',
|
||||
actions=['change']
|
||||
)
|
||||
obj_perm.save()
|
||||
obj_perm.users.add(self.user)
|
||||
obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
|
||||
|
||||
self.assertHttpStatus(self.client.post(self._get_url('bulk_rename'), data), 302)
|
||||
|
||||
# Verify changelog message was recorded on each renamed object
|
||||
object_type = ObjectType.objects.get_for_model(self.model)
|
||||
for pk in pk_list:
|
||||
oc = ObjectChange.objects.filter(
|
||||
changed_object_type=object_type,
|
||||
changed_object_id=pk,
|
||||
action=ObjectChangeActionChoices.ACTION_UPDATE,
|
||||
).order_by('-time').first()
|
||||
self.assertIsNotNone(oc)
|
||||
self.assertEqual(oc.message, 'Bulk rename test message')
|
||||
|
||||
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
|
||||
def test_bulk_rename_objects_with_constrained_permission(self):
|
||||
objects = self._get_queryset().all()[:3]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from ipam.models import VLAN, VRF, VLANGroup, VLANTranslationPolicy
|
|||
from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm
|
||||
from netbox.forms.mixins import OwnerMixin
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import BulkRenameForm, add_blank_choice
|
||||
from utilities.forms import add_blank_choice
|
||||
from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
|
||||
from utilities.forms.rendering import FieldSet
|
||||
from utilities.forms.utils import get_capacity_unit_label
|
||||
|
|
@ -25,9 +25,7 @@ __all__ = (
|
|||
'ClusterGroupBulkEditForm',
|
||||
'ClusterTypeBulkEditForm',
|
||||
'VMInterfaceBulkEditForm',
|
||||
'VMInterfaceBulkRenameForm',
|
||||
'VirtualDiskBulkEditForm',
|
||||
'VirtualDiskBulkRenameForm',
|
||||
'VirtualMachineBulkEditForm',
|
||||
'VirtualMachineTypeBulkEditForm',
|
||||
)
|
||||
|
|
@ -343,13 +341,6 @@ class VMInterfaceBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
|
|||
self.fields['bridge'].widget.attrs['disabled'] = True
|
||||
|
||||
|
||||
class VMInterfaceBulkRenameForm(BulkRenameForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=VMInterface.objects.all(),
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
|
||||
|
||||
class VirtualDiskBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
|
||||
virtual_machine = forms.ModelChoiceField(
|
||||
label=_('Virtual machine'),
|
||||
|
|
@ -379,10 +370,3 @@ class VirtualDiskBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
|
|||
|
||||
# Set unit label based on configured DISK_BASE_UNIT (MB vs MiB)
|
||||
self.fields['size'].label = _('Size ({unit})').format(unit=get_capacity_unit_label(settings.DISK_BASE_UNIT))
|
||||
|
||||
|
||||
class VirtualDiskBulkRenameForm(BulkRenameForm):
|
||||
pk = forms.ModelMultipleChoiceField(
|
||||
queryset=VirtualDisk.objects.all(),
|
||||
widget=forms.MultipleHiddenInput()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -744,7 +744,6 @@ class VMInterfaceBulkEditView(generic.BulkEditView):
|
|||
class VMInterfaceBulkRenameView(generic.BulkRenameView):
|
||||
queryset = VMInterface.objects.all()
|
||||
filterset = filtersets.VMInterfaceFilterSet
|
||||
form = forms.VMInterfaceBulkRenameForm
|
||||
|
||||
|
||||
@register_model_view(VMInterface, 'bulk_delete', path='delete', detail=False)
|
||||
|
|
@ -817,7 +816,6 @@ class VirtualDiskBulkEditView(generic.BulkEditView):
|
|||
class VirtualDiskBulkRenameView(generic.BulkRenameView):
|
||||
queryset = VirtualDisk.objects.all()
|
||||
filterset = filtersets.VirtualDiskFilterSet
|
||||
form = forms.VirtualDiskBulkRenameForm
|
||||
|
||||
|
||||
@register_model_view(VirtualDisk, 'bulk_delete', path='delete', detail=False)
|
||||
|
|
|
|||
Loading…
Reference in New Issue