diff --git a/netbox/extras/context_managers.py b/netbox/extras/context_managers.py index 66b5ff94d..ef2a5d293 100644 --- a/netbox/extras/context_managers.py +++ b/netbox/extras/context_managers.py @@ -1,11 +1,14 @@ from contextlib import contextmanager +import threading 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 utilities.utils import curry from .webhooks import flush_webhooks +changelog_lock = threading.Lock() + @contextmanager def change_logging(request): @@ -15,27 +18,31 @@ def change_logging(request): :param request: WSGIRequest object with a unique `id` set """ + webhook_queue = [] - # Curry signals receivers to pass the current request - handle_changed_object = curry(_handle_changed_object, request, webhook_queue) - handle_deleted_object = curry(_handle_deleted_object, request, webhook_queue) - clear_webhook_queue = curry(_clear_webhook_queue, webhook_queue) + with changelog_lock: - # 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') + # Curry signals receivers to pass the current request + handle_changed_object = curry(_handle_changed_object, request, webhook_queue) + handle_deleted_object = curry(_handle_deleted_object, request, webhook_queue) + clear_webhook_queue = curry(_clear_webhook_queue, webhook_queue) - yield + # 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') - # 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(webhook_queue)