def __call__(self, request): # Initialize an empty list to cache objects being saved. _thread_locals.changed_objects = [] # Assign a random unique ID to the request. This will be used to associate multiple object changes made during # the same request. request.id = uuid.uuid4() # Connect our receivers to the post_save and post_delete signals. post_save.connect(handle_changed_object, dispatch_uid='handle_changed_object') pre_delete.connect(handle_deleted_object, dispatch_uid='handle_deleted_object') # Provide a hook for purging the change cache purge_changelog.connect(purge_objectchange_cache) # Process the request response = self.get_response(request) # If the change cache is empty, there's nothing more we need to do. if not _thread_locals.changed_objects: return response # Create records for any cached objects that were changed. for instance, action in _thread_locals.changed_objects: # Refresh cached custom field values if action in [ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE]: if hasattr(instance, 'cache_custom_fields'): instance.cache_custom_fields() # Record an ObjectChange if applicable if hasattr(instance, 'to_objectchange'): objectchange = instance.to_objectchange(action) objectchange.user = request.user objectchange.request_id = request.id objectchange.save() # Enqueue webhooks enqueue_webhooks(instance, request.user, request.id, action) # Increment metric counters if action == ObjectChangeActionChoices.ACTION_CREATE: model_inserts.labels(instance._meta.model_name).inc() elif action == ObjectChangeActionChoices.ACTION_UPDATE: model_updates.labels(instance._meta.model_name).inc() elif action == ObjectChangeActionChoices.ACTION_DELETE: model_deletes.labels(instance._meta.model_name).inc() # Housekeeping: 1% chance of clearing out expired ObjectChanges. This applies only to requests which result in # one or more changes being logged. if settings.CHANGELOG_RETENTION and random.randint(1, 100) == 1: cutoff = timezone.now() - timedelta(days=settings.CHANGELOG_RETENTION) purged_count, _ = ObjectChange.objects.filter( time__lt=cutoff ).delete() return response
def _record_object_deleted(request, instance, **kwargs): # Record that the object was deleted if hasattr(instance, 'log_change'): instance.log_change(request.user, request.id, OBJECTCHANGE_ACTION_DELETE) # Enqueue webhooks enqueue_webhooks(instance, request.user, request.id, OBJECTCHANGE_ACTION_DELETE) # Increment metric counters model_deletes.labels(instance._meta.model_name).inc()
def _handle_deleted_object(request, sender, instance, **kwargs): """ Fires when an object is deleted. """ # Record an ObjectChange if applicable if hasattr(instance, 'to_objectchange'): objectchange = instance.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE) objectchange.user = request.user objectchange.request_id = request.id objectchange.save() # Enqueue webhooks enqueue_webhooks(instance, request.user, request.id, ObjectChangeActionChoices.ACTION_DELETE) # Increment metric counters model_deletes.labels(instance._meta.model_name).inc()
def _record_object_deleted(request, instance, **kwargs): # Force resolution of request.user in case it's still a SimpleLazyObject. This seems to happen # occasionally during tests, but haven't been able to determine why. # Note: We exclude SAML2 login flow deletions, as the call to request.session.flush() throws this assertion if request.path_info != '/saml2_auth/acs/': assert request.user.is_authenticated # Record that the object was deleted if hasattr(instance, 'log_change'): instance.log_change(request.user, request.id, OBJECTCHANGE_ACTION_DELETE) # Enqueue webhooks enqueue_webhooks(instance, request.user, request.id, OBJECTCHANGE_ACTION_DELETE) # Increment metric counters model_deletes.labels(instance._meta.model_name).inc()
def _handle_deleted_object(request, sender, instance, **kwargs): """ Fires when an object is deleted. """ # Ignore object without the right method if not hasattr(instance, "to_objectchange"): return # Record an object change change = instance.to_objectchange(ObjectChangeAction.DELETE) change.user = request.user change.request_id = request.id change.save() # Enqueue webhooks enqueue_webhooks(instance, request.user, request.id, ObjectChangeAction.DELETE) # Increment metric counters model_deletes.labels(instance._meta.model_name).inc()