Просмотр исходного кода

Permanently connect change logging & webhook receivers

jeremystretch 3 лет назад
Родитель
Сommit
4a95cfd1c4
3 измененных файлов с 26 добавлено и 26 удалено
  1. 0 16
      netbox/extras/context_managers.py
  2. 25 9
      netbox/extras/signals.py
  3. 1 1
      netbox/netbox/context.py

+ 0 - 16
netbox/extras/context_managers.py

@@ -1,8 +1,5 @@
 from contextlib import contextmanager
 
-from django.db.models.signals import m2m_changed, pre_delete, post_save
-
-from extras.signals import clear_webhooks, clear_webhook_queue, handle_changed_object, handle_deleted_object
 from netbox.context import current_request, webhooks_queue
 from .webhooks import flush_webhooks
 
@@ -18,21 +15,8 @@ def change_logging(request):
     current_request.set(request)
     webhooks_queue.set([])
 
-    # Connect our receivers to the post_save and post_delete signals.
-    post_save.connect(handle_changed_object, dispatch_uid='handle_changed_object')
-    m2m_changed.connect(handle_changed_object, dispatch_uid='handle_changed_object')
-    pre_delete.connect(handle_deleted_object, dispatch_uid='handle_deleted_object')
-    clear_webhooks.connect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
-
     yield
 
-    # Disconnect change logging signals. This is necessary to avoid recording any errant
-    # changes during test cleanup.
-    post_save.disconnect(handle_changed_object, dispatch_uid='handle_changed_object')
-    m2m_changed.disconnect(handle_changed_object, dispatch_uid='handle_changed_object')
-    pre_delete.disconnect(handle_deleted_object, dispatch_uid='handle_deleted_object')
-    clear_webhooks.disconnect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
-
     # Flush queued webhooks to RQ
     flush_webhooks(webhooks_queue.get())
 

+ 25 - 9
netbox/extras/signals.py

@@ -14,6 +14,7 @@ from .choices import ObjectChangeActionChoices
 from .models import ConfigRevision, CustomField, ObjectChange
 from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
 
+
 #
 # Change logging/webhooks
 #
@@ -22,22 +23,32 @@ from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
 clear_webhooks = Signal()
 
 
+def is_same_object(instance, webhook_data, request_id):
+    """
+    Compare the given instance to the most recent queued webhook object, returning True
+    if they match. This check is used to avoid creating duplicate webhook entries.
+    """
+    return (
+        ContentType.objects.get_for_model(instance) == webhook_data['content_type'] and
+        instance.pk == webhook_data['object_id'] and
+        request_id == webhook_data['request_id']
+    )
+
+
+@receiver((post_save, m2m_changed))
 def handle_changed_object(sender, instance, **kwargs):
     """
     Fires when an object is created or updated.
     """
+    m2m_changed = False
+
     if not hasattr(instance, 'to_objectchange'):
         return
 
+    # Get the current request, or bail if not set
     request = current_request.get()
-    m2m_changed = False
-
-    def is_same_object(instance, webhook_data):
-        return (
-            ContentType.objects.get_for_model(instance) == webhook_data['content_type'] and
-            instance.pk == webhook_data['object_id'] and
-            request.id == webhook_data['request_id']
-        )
+    if request is None:
+        return
 
     # Determine the type of change being made
     if kwargs.get('created'):
@@ -69,7 +80,7 @@ def handle_changed_object(sender, instance, **kwargs):
 
     # If this is an M2M change, update the previously queued webhook (from post_save)
     queue = webhooks_queue.get()
-    if m2m_changed and queue and is_same_object(instance, queue[-1]):
+    if m2m_changed and queue and is_same_object(instance, queue[-1], request.id):
         instance.refresh_from_db()  # Ensure that we're working with fresh M2M assignments
         queue[-1]['data'] = serialize_for_webhook(instance)
         queue[-1]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange']
@@ -84,6 +95,7 @@ def handle_changed_object(sender, instance, **kwargs):
         model_updates.labels(instance._meta.model_name).inc()
 
 
+@receiver(pre_delete)
 def handle_deleted_object(sender, instance, **kwargs):
     """
     Fires when an object is deleted.
@@ -91,7 +103,10 @@ def handle_deleted_object(sender, instance, **kwargs):
     if not hasattr(instance, 'to_objectchange'):
         return
 
+    # Get the current request, or bail if not set
     request = current_request.get()
+    if request is None:
+        return
 
     # Record an ObjectChange if applicable
     if hasattr(instance, 'to_objectchange'):
@@ -109,6 +124,7 @@ def handle_deleted_object(sender, instance, **kwargs):
     model_deletes.labels(instance._meta.model_name).inc()
 
 
+@receiver(clear_webhooks)
 def clear_webhook_queue(sender, **kwargs):
     """
     Delete any queued webhooks (e.g. because of an aborted bulk transaction)

+ 1 - 1
netbox/netbox/context.py

@@ -6,5 +6,5 @@ __all__ = (
 )
 
 
-current_request = ContextVar('current_request')
+current_request = ContextVar('current_request', default=None)
 webhooks_queue = ContextVar('webhooks_queue')