Skip to content

Commit 4a95cfd

Browse files
committed
Permanently connect change logging & webhook receivers
1 parent cd89431 commit 4a95cfd

File tree

3 files changed

+26
-26
lines changed

3 files changed

+26
-26
lines changed

netbox/extras/context_managers.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
from contextlib import contextmanager
22

3-
from django.db.models.signals import m2m_changed, pre_delete, post_save
4-
5-
from extras.signals import clear_webhooks, clear_webhook_queue, handle_changed_object, handle_deleted_object
63
from netbox.context import current_request, webhooks_queue
74
from .webhooks import flush_webhooks
85

@@ -18,21 +15,8 @@ def change_logging(request):
1815
current_request.set(request)
1916
webhooks_queue.set([])
2017

21-
# Connect our receivers to the post_save and post_delete signals.
22-
post_save.connect(handle_changed_object, dispatch_uid='handle_changed_object')
23-
m2m_changed.connect(handle_changed_object, dispatch_uid='handle_changed_object')
24-
pre_delete.connect(handle_deleted_object, dispatch_uid='handle_deleted_object')
25-
clear_webhooks.connect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
26-
2718
yield
2819

29-
# Disconnect change logging signals. This is necessary to avoid recording any errant
30-
# changes during test cleanup.
31-
post_save.disconnect(handle_changed_object, dispatch_uid='handle_changed_object')
32-
m2m_changed.disconnect(handle_changed_object, dispatch_uid='handle_changed_object')
33-
pre_delete.disconnect(handle_deleted_object, dispatch_uid='handle_deleted_object')
34-
clear_webhooks.disconnect(clear_webhook_queue, dispatch_uid='clear_webhook_queue')
35-
3620
# Flush queued webhooks to RQ
3721
flush_webhooks(webhooks_queue.get())
3822

netbox/extras/signals.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from .models import ConfigRevision, CustomField, ObjectChange
1515
from .webhooks import enqueue_object, get_snapshots, serialize_for_webhook
1616

17+
1718
#
1819
# Change logging/webhooks
1920
#
@@ -22,22 +23,32 @@
2223
clear_webhooks = Signal()
2324

2425

26+
def is_same_object(instance, webhook_data, request_id):
27+
"""
28+
Compare the given instance to the most recent queued webhook object, returning True
29+
if they match. This check is used to avoid creating duplicate webhook entries.
30+
"""
31+
return (
32+
ContentType.objects.get_for_model(instance) == webhook_data['content_type'] and
33+
instance.pk == webhook_data['object_id'] and
34+
request_id == webhook_data['request_id']
35+
)
36+
37+
38+
@receiver((post_save, m2m_changed))
2539
def handle_changed_object(sender, instance, **kwargs):
2640
"""
2741
Fires when an object is created or updated.
2842
"""
43+
m2m_changed = False
44+
2945
if not hasattr(instance, 'to_objectchange'):
3046
return
3147

48+
# Get the current request, or bail if not set
3249
request = current_request.get()
33-
m2m_changed = False
34-
35-
def is_same_object(instance, webhook_data):
36-
return (
37-
ContentType.objects.get_for_model(instance) == webhook_data['content_type'] and
38-
instance.pk == webhook_data['object_id'] and
39-
request.id == webhook_data['request_id']
40-
)
50+
if request is None:
51+
return
4152

4253
# Determine the type of change being made
4354
if kwargs.get('created'):
@@ -69,7 +80,7 @@ def is_same_object(instance, webhook_data):
6980

7081
# If this is an M2M change, update the previously queued webhook (from post_save)
7182
queue = webhooks_queue.get()
72-
if m2m_changed and queue and is_same_object(instance, queue[-1]):
83+
if m2m_changed and queue and is_same_object(instance, queue[-1], request.id):
7384
instance.refresh_from_db() # Ensure that we're working with fresh M2M assignments
7485
queue[-1]['data'] = serialize_for_webhook(instance)
7586
queue[-1]['snapshots']['postchange'] = get_snapshots(instance, action)['postchange']
@@ -84,14 +95,18 @@ def is_same_object(instance, webhook_data):
8495
model_updates.labels(instance._meta.model_name).inc()
8596

8697

98+
@receiver(pre_delete)
8799
def handle_deleted_object(sender, instance, **kwargs):
88100
"""
89101
Fires when an object is deleted.
90102
"""
91103
if not hasattr(instance, 'to_objectchange'):
92104
return
93105

106+
# Get the current request, or bail if not set
94107
request = current_request.get()
108+
if request is None:
109+
return
95110

96111
# Record an ObjectChange if applicable
97112
if hasattr(instance, 'to_objectchange'):
@@ -109,6 +124,7 @@ def handle_deleted_object(sender, instance, **kwargs):
109124
model_deletes.labels(instance._meta.model_name).inc()
110125

111126

127+
@receiver(clear_webhooks)
112128
def clear_webhook_queue(sender, **kwargs):
113129
"""
114130
Delete any queued webhooks (e.g. because of an aborted bulk transaction)

netbox/netbox/context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
)
77

88

9-
current_request = ContextVar('current_request')
9+
current_request = ContextVar('current_request', default=None)
1010
webhooks_queue = ContextVar('webhooks_queue')

0 commit comments

Comments
 (0)