Przeglądaj źródła

Fixes: #18717 - On delete signal handling, manually save the related object in a ManyToOneRel to trigger a change record (#19308)

* On delete signal handling, manually save the related object in a ManyToOneRel to trigger a change record

* Only set remote field to None if null=True on the relation
bctiemann 9 miesięcy temu
rodzic
commit
bee004fc0c
1 zmienionych plików z 9 dodań i 3 usunięć
  1. 9 3
      netbox/core/signals.py

+ 9 - 3
netbox/core/signals.py

@@ -2,7 +2,7 @@ import logging
 
 
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
-from django.db.models.fields.reverse_related import ManyToManyRel
+from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel
 from django.db.models.signals import m2m_changed, post_save, pre_delete
 from django.db.models.signals import m2m_changed, post_save, pre_delete
 from django.dispatch import receiver, Signal
 from django.dispatch import receiver, Signal
 from django.utils.translation import gettext_lazy as _
 from django.utils.translation import gettext_lazy as _
@@ -146,8 +146,10 @@ def handle_deleted_object(sender, instance, **kwargs):
     # instance being deleted, and explicitly call .remove() on the remote M2M field to delete
     # instance being deleted, and explicitly call .remove() on the remote M2M field to delete
     # the association. This triggers an m2m_changed signal with the `post_remove` action type
     # the association. This triggers an m2m_changed signal with the `post_remove` action type
     # 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
+    # and save it to trigger a change record on that object.
     for relation in instance._meta.related_objects:
     for relation in instance._meta.related_objects:
-        if type(relation) is not ManyToManyRel:
+        if type(relation) not in [ManyToManyRel, ManyToOneRel]:
             continue
             continue
         related_model = relation.related_model
         related_model = relation.related_model
         related_field_name = relation.remote_field.name
         related_field_name = relation.remote_field.name
@@ -157,7 +159,11 @@ def handle_deleted_object(sender, instance, **kwargs):
             continue
             continue
         for obj in related_model.objects.filter(**{related_field_name: instance.pk}):
         for obj in related_model.objects.filter(**{related_field_name: instance.pk}):
             obj.snapshot()  # Ensure the change record includes the "before" state
             obj.snapshot()  # Ensure the change record includes the "before" state
-            getattr(obj, related_field_name).remove(instance)
+            if type(relation) is ManyToManyRel:
+                getattr(obj, related_field_name).remove(instance)
+            elif type(relation) is ManyToOneRel and relation.field.null is True:
+                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()