21390 skip m2m processing for internal models to avoid extraneous ObjectChange records

This commit is contained in:
Arthur
2026-02-12 14:30:19 -08:00
parent 0bb22dee0c
commit 213a40e98b

View File

@@ -209,22 +209,28 @@ def handle_deleted_object(sender, instance, **kwargs):
# for the forward direction of the relationship, ensuring that the change is recorded. # for the forward direction of the relationship, ensuring that the change is recorded.
# Similarly, for many-to-one relationships, we set the value on the related object to None # Similarly, for many-to-one relationships, we set the value on the related object to None
# and save it to trigger a change record on that object. # and save it to trigger a change record on that object.
for relation in instance._meta.related_objects: #
if type(relation) not in [ManyToManyRel, ManyToOneRel]: # Skip this for private models (e.g. CablePath) whose lifecycle is an internal
continue # implementation detail. Django's on_delete handlers (e.g. SET_NULL) already take
related_model = relation.related_model # care of the database integrity; recording changelog entries for the related
related_field_name = relation.remote_field.name # objects would be spurious. (Ref: #21390)
if not issubclass(related_model, ChangeLoggingMixin): if not getattr(instance, '_netbox_private', False):
# We only care about triggering the m2m_changed signal for models which support for relation in instance._meta.related_objects:
# change logging if type(relation) not in [ManyToManyRel, ManyToOneRel]:
continue continue
for obj in related_model.objects.filter(**{related_field_name: instance.pk}): related_model = relation.related_model
obj.snapshot() # Ensure the change record includes the "before" state related_field_name = relation.remote_field.name
if type(relation) is ManyToManyRel: if not issubclass(related_model, ChangeLoggingMixin):
getattr(obj, related_field_name).remove(instance) # We only care about triggering the m2m_changed signal for models which support
elif type(relation) is ManyToOneRel and relation.null and relation.on_delete not in (CASCADE, RESTRICT): # change logging
setattr(obj, related_field_name, None) continue
obj.save() for obj in related_model.objects.filter(**{related_field_name: instance.pk}):
obj.snapshot() # Ensure the change record includes the "before" state
if type(relation) is ManyToManyRel:
getattr(obj, related_field_name).remove(instance)
elif type(relation) is ManyToOneRel and relation.null and relation.on_delete not in (CASCADE, RESTRICT):
setattr(obj, related_field_name, None)
obj.save()
# Enqueue the object for event processing # Enqueue the object for event processing
queue = events_queue.get() queue = events_queue.get()