Add changelog message support for bulk edit & bulk delete

This commit is contained in:
Jeremy Stretch 2025-07-18 10:31:42 -04:00
parent 1b11895c90
commit 0703fe7852
6 changed files with 24 additions and 15 deletions

View File

@ -100,7 +100,7 @@ class NetBoxModelImportForm(CSVModelForm, NetBoxModelForm):
return customfield.to_form_field(for_csv_import=True)
class NetBoxModelBulkEditForm(CustomFieldsMixin, BulkEditForm):
class NetBoxModelBulkEditForm(ChangeLoggingMixin, CustomFieldsMixin, BulkEditForm):
"""
Base form for modifying multiple NetBox objects (of the same type) in bulk via the UI. Adds support for custom
fields and adding/removing tags.

View File

@ -20,13 +20,6 @@ class ChangeLoggingMixin(forms.Form):
max_length=200
)
def clean(self):
# Attach the changelog message (if any) to the instance
self.instance._changelog_message = self.cleaned_data.pop('changelog_message', None)
return self.cleaned_data
class CustomFieldsMixin:
"""

View File

@ -21,6 +21,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.mixins import ChangeLoggingMixin
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport, BulkRename
from utilities.error_handlers import handle_protectederror
from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation
@ -622,6 +623,9 @@ class BulkEditView(GetReturnURLMixin, BaseMultiObjectView):
if hasattr(obj, 'snapshot'):
obj.snapshot()
# Attach the changelog message (if any) to the object
obj._changelog_message = form.cleaned_data.get('changelog_message')
# Update standard fields. If a field is listed in _nullify, delete its value.
for name, model_field in model_fields.items():
# Handle nullification
@ -892,7 +896,7 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView):
"""
Provide a standard bulk delete form if none has been specified for the view
"""
class BulkDeleteForm(BackgroundJobMixin, ConfirmationForm):
class BulkDeleteForm(BackgroundJobMixin, ChangeLoggingMixin, ConfirmationForm):
pk = ModelMultipleChoiceField(queryset=self.queryset, widget=MultipleHiddenInput)
return BulkDeleteForm
@ -939,9 +943,15 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView):
try:
with transaction.atomic(using=router.db_for_write(model)):
for obj in queryset:
# Take a snapshot of change-logged models
if hasattr(obj, 'snapshot'):
obj.snapshot()
# Attach the changelog message (if any) to the object
obj._changelog_message = form.cleaned_data.get('changelog_message')
# Delete the object
obj.delete()
except (ProtectedError, RestrictedError) as e:

View File

@ -288,6 +288,9 @@ class ObjectEditView(GetReturnURLMixin, BaseObjectView):
if form.is_valid():
logger.debug("Form validation was successful")
# Record changelog message (if any)
obj._changelog_message = form.cleaned_data.pop('changelog_message', '')
try:
with transaction.atomic(using=router.db_for_write(model)):
object_created = form.instance.pk is None
@ -463,22 +466,23 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView):
obj = self.get_object(**kwargs)
form = DeleteForm(request.POST)
# Take a snapshot of change-logged models
if hasattr(obj, 'snapshot'):
obj.snapshot()
if form.is_valid():
logger.debug("Form validation was successful")
# Take a snapshot of change-logged models
if hasattr(obj, 'snapshot'):
obj.snapshot()
# Record changelog message (if any)
obj._changelog_message = form.cleaned_data.pop('changelog_message', '')
# Delete the object
try:
obj.delete()
except (ProtectedError, RestrictedError) as e:
logger.info(f"Caught {type(e)} while attempting to delete objects")
handle_protectederror([obj], request, e)
return redirect(obj.get_absolute_url())
except AbortRequest as e:
logger.debug(e.message)
messages.error(request, mark_safe(e.message))

View File

@ -67,6 +67,7 @@ Context:
{# Meta fields #}
<div class="bg-primary-subtle border border-primary rounded-1 pt-3 mb-3">
{% render_field form.changelog_message %}
{% render_field form.background_job %}
</div>

View File

@ -104,6 +104,7 @@ Context:
{# Meta fields #}
<div class="bg-primary-subtle border border-primary rounded-1 pt-3 mb-3">
{% render_field form.changelog_message %}
{% render_field form.background_job %}
</div>