mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-18 05:28:16 -06:00
14147 Prevent logging to Change Log when no changes are made
This commit is contained in:
parent
d428dd172c
commit
f6f1fa799e
@ -80,6 +80,17 @@ changes in the database indefinitely.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## CHANGELOG_SKIP_EMPTY_CHANGES
|
||||||
|
|
||||||
|
Default: False
|
||||||
|
|
||||||
|
Enables skipping the creation of logged changes on updates if there were no modifications to the object.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
As a side-effect of turning this on, the `last_updated` field will not be included in the change log record.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## DATA_UPLOAD_MAX_MEMORY_SIZE
|
## DATA_UPLOAD_MAX_MEMORY_SIZE
|
||||||
|
|
||||||
Default: `2621440` (2.5 MB)
|
Default: `2621440` (2.5 MB)
|
||||||
|
@ -239,7 +239,8 @@ class CircuitTermination(
|
|||||||
raise ValidationError("A circuit termination cannot attach to both a site and a provider network.")
|
raise ValidationError("A circuit termination cannot attach to both a site and a provider network.")
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.circuit
|
objectchange.related_object = self.circuit
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
|
@ -386,7 +386,8 @@ class CableTermination(ChangeLoggedModel):
|
|||||||
cache_related_objects.alters_data = True
|
cache_related_objects.alters_data = True
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.termination
|
objectchange.related_object = self.termination
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
|
@ -91,7 +91,8 @@ class ComponentTemplateModel(ChangeLoggedModel, TrackingModelMixin):
|
|||||||
self._original_device_type = self.__dict__.get('device_type_id')
|
self._original_device_type = self.__dict__.get('device_type_id')
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.device_type
|
objectchange.related_object = self.device_type
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
@ -138,7 +139,8 @@ class ModularComponentTemplateModel(ComponentTemplateModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
if self.device_type is not None:
|
if self.device_type is not None:
|
||||||
objectchange.related_object = self.device_type
|
objectchange.related_object = self.device_type
|
||||||
elif self.module_type is not None:
|
elif self.module_type is not None:
|
||||||
|
@ -93,7 +93,8 @@ class ComponentModel(NetBoxModel):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.device
|
objectchange.related_object = self.device
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
|
@ -688,7 +688,8 @@ class ImageAttachment(ChangeLoggedModel):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.parent
|
objectchange.related_object = self.parent
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
|
@ -80,9 +80,10 @@ def handle_changed_object(sender, instance, **kwargs):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
objectchange = instance.to_objectchange(action)
|
objectchange = instance.to_objectchange(action)
|
||||||
objectchange.user = request.user
|
if objectchange:
|
||||||
objectchange.request_id = request.id
|
objectchange.user = request.user
|
||||||
objectchange.save()
|
objectchange.request_id = request.id
|
||||||
|
objectchange.save()
|
||||||
|
|
||||||
# If this is an M2M change, update the previously queued webhook (from post_save)
|
# If this is an M2M change, update the previously queued webhook (from post_save)
|
||||||
queue = events_queue.get()
|
queue = events_queue.get()
|
||||||
|
@ -902,7 +902,8 @@ class IPAddress(PrimaryModel):
|
|||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.assigned_object
|
objectchange.related_object = self.assigned_object
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ from core.choices import JobStatusChoices
|
|||||||
from core.models import ContentType
|
from core.models import ContentType
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
from extras.utils import is_taggable, register_features
|
from extras.utils import is_taggable, register_features
|
||||||
|
from netbox.config import get_config
|
||||||
from netbox.registry import registry
|
from netbox.registry import registry
|
||||||
from netbox.signals import post_clean
|
from netbox.signals import post_clean
|
||||||
from utilities.json import CustomFieldJSONEncoder
|
from utilities.json import CustomFieldJSONEncoder
|
||||||
@ -84,6 +85,15 @@ class ChangeLoggingMixin(models.Model):
|
|||||||
by ChangeLoggingMiddleware.
|
by ChangeLoggingMiddleware.
|
||||||
"""
|
"""
|
||||||
from extras.models import ObjectChange
|
from extras.models import ObjectChange
|
||||||
|
|
||||||
|
postchange_data = None
|
||||||
|
if action in (ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE):
|
||||||
|
postchange_data = self.serialize_object()
|
||||||
|
|
||||||
|
if get_config().CHANGELOG_SKIP_EMPTY_CHANGES and action == ObjectChangeActionChoices.ACTION_UPDATE and hasattr(self, '_prechange_snapshot'):
|
||||||
|
if postchange_data == self._prechange_snapshot:
|
||||||
|
return None
|
||||||
|
|
||||||
objectchange = ObjectChange(
|
objectchange = ObjectChange(
|
||||||
changed_object=self,
|
changed_object=self,
|
||||||
object_repr=str(self)[:200],
|
object_repr=str(self)[:200],
|
||||||
@ -92,7 +102,7 @@ class ChangeLoggingMixin(models.Model):
|
|||||||
if hasattr(self, '_prechange_snapshot'):
|
if hasattr(self, '_prechange_snapshot'):
|
||||||
objectchange.prechange_data = self._prechange_snapshot
|
objectchange.prechange_data = self._prechange_snapshot
|
||||||
if action in (ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE):
|
if action in (ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE):
|
||||||
objectchange.postchange_data = self.serialize_object()
|
objectchange.postchange_data = postchange_data
|
||||||
|
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
|
@ -177,6 +177,7 @@ STORAGE_CONFIG = getattr(configuration, 'STORAGE_CONFIG', {})
|
|||||||
TIME_FORMAT = getattr(configuration, 'TIME_FORMAT', 'g:i a')
|
TIME_FORMAT = getattr(configuration, 'TIME_FORMAT', 'g:i a')
|
||||||
TIME_ZONE = getattr(configuration, 'TIME_ZONE', 'UTC')
|
TIME_ZONE = getattr(configuration, 'TIME_ZONE', 'UTC')
|
||||||
ENABLE_LOCALIZATION = getattr(configuration, 'ENABLE_LOCALIZATION', False)
|
ENABLE_LOCALIZATION = getattr(configuration, 'ENABLE_LOCALIZATION', False)
|
||||||
|
CHANGELOG_SKIP_EMPTY_CHANGES = getattr(configuration, 'CHANGELOG_SKIP_EMPTY_CHANGES', False)
|
||||||
|
|
||||||
# Check for hard-coded dynamic config parameters
|
# Check for hard-coded dynamic config parameters
|
||||||
for param in PARAMS:
|
for param in PARAMS:
|
||||||
|
@ -171,6 +171,7 @@ class ContactAssignment(CustomFieldsMixin, ExportTemplatesMixin, TagsMixin, Chan
|
|||||||
)
|
)
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.object
|
objectchange.related_object = self.object
|
||||||
return objectchange
|
return objectchange
|
||||||
|
@ -159,6 +159,9 @@ def serialize_object(obj, resolve_tags=True, extra=None):
|
|||||||
for field in ['level', 'lft', 'rght', 'tree_id']:
|
for field in ['level', 'lft', 'rght', 'tree_id']:
|
||||||
data.pop(field)
|
data.pop(field)
|
||||||
|
|
||||||
|
if get_config().CHANGELOG_SKIP_EMPTY_CHANGES and 'last_updated' in data:
|
||||||
|
data.pop('last_updated')
|
||||||
|
|
||||||
# Include custom_field_data as "custom_fields"
|
# Include custom_field_data as "custom_fields"
|
||||||
if hasattr(obj, 'custom_field_data'):
|
if hasattr(obj, 'custom_field_data'):
|
||||||
data['custom_fields'] = data.pop('custom_field_data')
|
data['custom_fields'] = data.pop('custom_field_data')
|
||||||
|
@ -298,7 +298,8 @@ class ComponentModel(NetBoxModel):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.virtual_machine
|
objectchange.related_object = self.virtual_machine
|
||||||
return objectchange
|
return objectchange
|
||||||
|
|
||||||
|
@ -178,6 +178,7 @@ class TunnelTermination(CustomFieldsMixin, CustomLinksMixin, TagsMixin, ChangeLo
|
|||||||
})
|
})
|
||||||
|
|
||||||
def to_objectchange(self, action):
|
def to_objectchange(self, action):
|
||||||
objectchange = super().to_objectchange(action)
|
if (objectchange := super().to_objectchange(action)) is None:
|
||||||
|
return None
|
||||||
objectchange.related_object = self.tunnel
|
objectchange.related_object = self.tunnel
|
||||||
return objectchange
|
return objectchange
|
||||||
|
Loading…
Reference in New Issue
Block a user