def pre_save_signal(sender, instance, **kwargs) -> None: if not settings.AUDIT_LOG_ENABLED: return if not AuditLogMiddleware.is_request(): logger.debug("Not a request") return model_name = get_model_name(instance) if exclude_model(model_name): logger.debug(f"{model_name} ignored as per settings") return get_or_create_meta(instance) instance._meta.dal.event = None operation = Operation.UPDATE try: pre = sender.objects.get(pk=instance.pk) except ObjectDoesNotExist: # __dict__ is used on pre, therefore we need to create a function # that uses __dict__ too, but returns nothing. pre = lambda _: None operation = Operation.INSERT changes = {} if operation not in {Operation.INSERT, Operation.DELETE}: old, new = remove_non_member_fields(pre.__dict__), remove_non_member_fields(instance.__dict__) try: changes = dict(set(new.items()).difference(old.items())) except TypeError: # handle non-hashable types old_hashable, old_non_hashable = seperate_hashable_dict(old) new_hashable, new_non_hashable = seperate_hashable_dict(new) changes = dict(set(new_hashable.items()).difference(old_hashable.items())) changes.update({k: v for k, v in new_non_hashable.items() if v != old_non_hashable.get(k)}) excluded_fields = settings.AUDIT_LOG["models"]["exclude"]["fields"].get(model_name, []) for field in excluded_fields: if field in changes: changes[field] = "xxx" if not changes: logger.debug("No changes for model. Ignoring.") return current_user = AuditLogMiddleware.get_current_user() instance._meta.dal.event = Event(model=model_name, actor=current_user, entity_id=instance.pk, changes=changes)
def post_delete_signal(sender, instance, **kwargs) -> None: if not settings.AUDIT_LOG_ENABLED: return if not AuditLogMiddleware.is_request(): logger.debug("Not a request") return model_name = get_model_name(instance) if exclude_model(model_name): logger.debug(f"Ignoring {model_name}.") return event = instance._meta.dal.event _post_processor(instance, event, Operation.DELETE)
def post_save_signal(sender, instance, created, update_fields: frozenset, **kwargs): if not settings.AUDIT_LOG_ENABLED: return if not AuditLogMiddleware.is_request(): logger.debug("Not a request") return model_name = get_model_name(instance) if exclude_model(model_name): logger.debug(f"Ignoring {model_name}.") return operation = Operation.INSERT if created else Operation.UPDATE get_or_create_meta(instance) event = instance._meta.dal.event _post_processor(instance, event, operation)
def _post_processor(instance, event: Optional[Event], operation: Operation): request_id = AuditLogMiddleware.get_current_request_id() actor = AuditLogMiddleware.get_current_user() model_name = get_model_name(instance) if not event and operation != Operation.DELETE: logger.debug(f"Event not received for {operation}. Ignoring.") return try: changes = json.dumps(event.changes if event else dict(), cls=LogJsonEncoder) except Exception: logger.warning(f"Failed to log {event}", exc_info=True) return logger.info( f"AUDIT_LOG::{request_id}|{actor}|{operation.value}|{model_name}|ID:{instance.pk}|{changes}" )