From a8b11e45c1a11f77be45f09ca3a38f9f8f81ba6f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 19 Jun 2018 15:45:15 -0400 Subject: [PATCH] Record a unique request ID with each ObjectChange --- netbox/extras/admin.py | 6 +++--- netbox/extras/api/serializers.py | 6 +++++- netbox/extras/filters.py | 2 +- netbox/extras/middleware.py | 8 ++++++-- netbox/extras/migrations/0013_objectchange.py | 3 ++- netbox/extras/models.py | 3 +++ 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/netbox/extras/admin.py b/netbox/extras/admin.py index 3da723b7d..534057a8d 100644 --- a/netbox/extras/admin.py +++ b/netbox/extras/admin.py @@ -132,12 +132,12 @@ class TopologyMapAdmin(admin.ModelAdmin): @admin.register(ObjectChange) class ObjectChangeAdmin(admin.ModelAdmin): actions = None - fields = ['time', 'content_type', 'display_object', 'action', 'display_user', 'object_data'] - list_display = ['time', 'content_type', 'display_object', 'display_action', 'display_user'] + fields = ['time', 'content_type', 'display_object', 'action', 'display_user', 'request_id', 'object_data'] + list_display = ['time', 'content_type', 'display_object', 'display_action', 'display_user', 'request_id'] list_filter = ['time', 'action', 'user__username'] list_select_related = ['content_type', 'user'] readonly_fields = fields - search_fields = ['user_name', 'object_repr'] + search_fields = ['user_name', 'object_repr', 'request_id'] def has_add_permission(self, request): return False diff --git a/netbox/extras/api/serializers.py b/netbox/extras/api/serializers.py index 8f9822a90..10afee954 100644 --- a/netbox/extras/api/serializers.py +++ b/netbox/extras/api/serializers.py @@ -167,12 +167,16 @@ class ObjectChangeSerializer(serializers.ModelSerializer): class Meta: model = ObjectChange - fields = ['id', 'time', 'user', 'user_name', 'action', 'content_type', 'changed_object', 'object_data'] + fields = [ + 'id', 'time', 'user', 'user_name', 'request_id', 'action', 'content_type', 'changed_object', 'object_data', + ] def get_changed_object(self, obj): """ Serialize a nested representation of the changed object. """ + if obj.changed_object is None: + return None serializer = get_serializer_for_model(obj.changed_object, prefix='Nested') if serializer is None: return obj.object_repr diff --git a/netbox/extras/filters.py b/netbox/extras/filters.py index 679a251f2..52eef54b7 100644 --- a/netbox/extras/filters.py +++ b/netbox/extras/filters.py @@ -132,7 +132,7 @@ class ObjectChangeFilter(django_filters.FilterSet): class Meta: model = ObjectChange - fields = ['user_name', 'action', 'content_type', 'object_repr'] + fields = ['user', 'user_name', 'request_id', 'action', 'content_type', 'object_repr'] def search(self, queryset, name, value): if not value.strip(): diff --git a/netbox/extras/middleware.py b/netbox/extras/middleware.py index e0c7254c0..4492c550a 100644 --- a/netbox/extras/middleware.py +++ b/netbox/extras/middleware.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import json +import uuid from django.core.serializers import serialize from django.db.models.signals import post_delete, post_save @@ -11,7 +12,7 @@ from .constants import OBJECTCHANGE_ACTION_CREATE, OBJECTCHANGE_ACTION_DELETE, O from .models import ObjectChange -def record_object_change(user, instance, **kwargs): +def record_object_change(user, request_id, instance, **kwargs): """ Create an ObjectChange in response to an object being created or deleted. """ @@ -31,6 +32,7 @@ def record_object_change(user, instance, **kwargs): ObjectChange( user=user, + request_id=request_id, changed_object=instance, action=action, object_data=object_data @@ -53,9 +55,11 @@ class ChangeLoggingMiddleware(object): # detail, see https://stackoverflow.com/questions/26240832/ user = SimpleLazyObject(lambda: get_user(request)) + request_id = uuid.uuid4() + # Django doesn't provide any request context with the post_save/post_delete signals, so we curry # record_object_change() to include the user associated with the current request. - _record_object_change = curry(record_object_change, user) + _record_object_change = curry(record_object_change, user, request_id) post_save.connect(_record_object_change, dispatch_uid='record_object_saved') post_delete.connect(_record_object_change, dispatch_uid='record_object_deleted') diff --git a/netbox/extras/migrations/0013_objectchange.py b/netbox/extras/migrations/0013_objectchange.py index fdaf7dfd5..a8a7d7ee3 100644 --- a/netbox/extras/migrations/0013_objectchange.py +++ b/netbox/extras/migrations/0013_objectchange.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-06-13 20:05 +# Generated by Django 1.11.12 on 2018-06-19 19:34 from __future__ import unicode_literals from django.conf import settings @@ -23,6 +23,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('time', models.DateTimeField(auto_now_add=True)), ('user_name', models.CharField(editable=False, max_length=150)), + ('request_id', models.UUIDField(editable=False)), ('action', models.PositiveSmallIntegerField(choices=[(1, 'Created'), (2, 'Updated'), (3, 'Deleted')])), ('object_id', models.PositiveIntegerField()), ('object_repr', models.CharField(editable=False, max_length=200)), diff --git a/netbox/extras/models.py b/netbox/extras/models.py index 759f90b4b..da7356540 100644 --- a/netbox/extras/models.py +++ b/netbox/extras/models.py @@ -681,6 +681,9 @@ class ObjectChange(models.Model): max_length=150, editable=False ) + request_id = models.UUIDField( + editable=False + ) action = models.PositiveSmallIntegerField( choices=OBJECTCHANGE_ACTION_CHOICES )