Enable changelog messages for single object operations

This commit is contained in:
Jeremy Stretch 2025-07-17 16:37:42 -04:00
parent 5e5c46f77c
commit 8d5436876e
6 changed files with 40 additions and 6 deletions

View File

@ -11,7 +11,7 @@ from extras.models import CustomField, Tag
from utilities.forms import BulkEditForm, CSVModelForm from utilities.forms import BulkEditForm, CSVModelForm
from utilities.forms.fields import CSVModelMultipleChoiceField, DynamicModelMultipleChoiceField from utilities.forms.fields import CSVModelMultipleChoiceField, DynamicModelMultipleChoiceField
from utilities.forms.mixins import CheckLastUpdatedMixin from utilities.forms.mixins import CheckLastUpdatedMixin
from .mixins import CustomFieldsMixin, SavedFiltersMixin, TagsMixin from .mixins import ChangeLoggingMixin, CustomFieldsMixin, SavedFiltersMixin, TagsMixin
__all__ = ( __all__ = (
'NetBoxModelForm', 'NetBoxModelForm',
@ -21,7 +21,7 @@ __all__ = (
) )
class NetBoxModelForm(CheckLastUpdatedMixin, CustomFieldsMixin, TagsMixin, forms.ModelForm): class NetBoxModelForm(ChangeLoggingMixin, CheckLastUpdatedMixin, CustomFieldsMixin, TagsMixin, forms.ModelForm):
""" """
Base form for creating & editing NetBox models. Extends Django's ModelForm to add support for custom fields. Base form for creating & editing NetBox models. Extends Django's ModelForm to add support for custom fields.

View File

@ -7,12 +7,27 @@ from extras.models import *
from utilities.forms.fields import DynamicModelMultipleChoiceField from utilities.forms.fields import DynamicModelMultipleChoiceField
__all__ = ( __all__ = (
'ChangeLoggingMixin',
'CustomFieldsMixin', 'CustomFieldsMixin',
'SavedFiltersMixin', 'SavedFiltersMixin',
'TagsMixin', 'TagsMixin',
) )
class ChangeLoggingMixin(forms.Form):
changelog_message = forms.CharField(
required=False,
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: class CustomFieldsMixin:
""" """
Extend a Form to include custom field support. Extend a Form to include custom field support.

View File

@ -63,6 +63,8 @@ class ChangeLoggingMixin(DeleteMixin, models.Model):
null=True null=True
) )
_changelog_message = None
class Meta: class Meta:
abstract = True abstract = True
@ -103,7 +105,8 @@ class ChangeLoggingMixin(DeleteMixin, models.Model):
objectchange = ObjectChange( objectchange = ObjectChange(
changed_object=self, changed_object=self,
object_repr=str(self)[:200], object_repr=str(self)[:200],
action=action action=action,
message=self._changelog_message or '',
) )
if hasattr(self, '_prechange_snapshot'): if hasattr(self, '_prechange_snapshot'):
objectchange.prechange_data = self._prechange_snapshot objectchange.prechange_data = self._prechange_snapshot

View File

@ -19,7 +19,7 @@ from netbox.object_actions import (
) )
from utilities.error_handlers import handle_protectederror from utilities.error_handlers import handle_protectederror
from utilities.exceptions import AbortRequest, PermissionsViolation from utilities.exceptions import AbortRequest, PermissionsViolation
from utilities.forms import ConfirmationForm, restrict_form_fields from utilities.forms import DeleteForm, restrict_form_fields
from utilities.htmx import htmx_partial from utilities.htmx import htmx_partial
from utilities.permissions import get_permission_for_model from utilities.permissions import get_permission_for_model
from utilities.querydict import normalize_querydict, prepare_cloned_fields from utilities.querydict import normalize_querydict, prepare_cloned_fields
@ -422,7 +422,7 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView):
request: The current request request: The current request
""" """
obj = self.get_object(**kwargs) obj = self.get_object(**kwargs)
form = ConfirmationForm(initial=request.GET) form = DeleteForm(initial=request.GET)
try: try:
dependent_objects = self._get_dependent_objects(obj) dependent_objects = self._get_dependent_objects(obj)
@ -461,7 +461,7 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView):
""" """
logger = logging.getLogger('netbox.views.ObjectDeleteView') logger = logging.getLogger('netbox.views.ObjectDeleteView')
obj = self.get_object(**kwargs) obj = self.get_object(**kwargs)
form = ConfirmationForm(request.POST) form = DeleteForm(request.POST)
# Take a snapshot of change-logged models # Take a snapshot of change-logged models
if hasattr(obj, 'snapshot'): if hasattr(obj, 'snapshot'):
@ -469,6 +469,7 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView):
if form.is_valid(): if form.is_valid():
logger.debug("Form validation was successful") logger.debug("Form validation was successful")
obj._changelog_message = form.cleaned_data.pop('changelog_message', '')
try: try:
obj.delete() obj.delete()

View File

@ -28,6 +28,10 @@
</div> </div>
{% endif %} {% endif %}
{% if form.changelog_message %}
{% render_field form.changelog_message %}
{% endif %}
{% else %} {% else %}
{# Render all fields in a single group #} {# Render all fields in a single group #}

View File

@ -10,6 +10,7 @@ __all__ = (
'BulkRenameForm', 'BulkRenameForm',
'ConfirmationForm', 'ConfirmationForm',
'CSVModelForm', 'CSVModelForm',
'DeleteForm',
'FilterForm', 'FilterForm',
'TableConfigForm', 'TableConfigForm',
) )
@ -30,6 +31,16 @@ class ConfirmationForm(forms.Form):
) )
class DeleteForm(ConfirmationForm):
"""
Confirm the deletion of an object, optionally providing a changelog message.
"""
changelog_message = forms.CharField(
required=False,
max_length=200
)
class BulkEditForm(BackgroundJobMixin, forms.Form): class BulkEditForm(BackgroundJobMixin, forms.Form):
""" """
Provides bulk edit support for objects. Provides bulk edit support for objects.