From 99847e5d40979a3046729ec3f3b8e29f1d953629 Mon Sep 17 00:00:00 2001 From: Tosinibikunle Date: Wed, 20 Aug 2025 15:53:04 +0100 Subject: [PATCH] fix: defer field deletion error in auditlog --- auditlog/models.py | 13 +++++++++++++ auditlog_tests/tests.py | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/auditlog/models.py b/auditlog/models.py index 01b54a0..6811544 100644 --- a/auditlog/models.py +++ b/auditlog/models.py @@ -240,7 +240,19 @@ class LogEntryManager(models.Manager): "fields", self._get_applicable_model_fields(instance, model_fields) ) + # safeguard: if fields explicitly set → only use them + if "fields" in kwargs: + allowed_fields = set(kwargs["fields"]) + else: + allowed_fields = {f.name for f in instance._meta.fields} + instance_copy = self._get_copy_with_python_typed_fields(instance) + + # drop any deferred field not in allowed_fields + for f in instance_copy._meta.fields: + if f.name not in allowed_fields and f.name in getattr(instance_copy, "deferred_fields", []): + setattr(instance_copy, f.name, None) + data = dict( json.loads(serializers.serialize("json", (instance_copy,), **kwargs))[0] ) @@ -251,6 +263,7 @@ class LogEntryManager(models.Manager): return data + def _get_copy_with_python_typed_fields(self, instance): """ Attempt to create copy of instance and coerce types on instance fields diff --git a/auditlog_tests/tests.py b/auditlog_tests/tests.py index a6cc29d..1ce3dfd 100644 --- a/auditlog_tests/tests.py +++ b/auditlog_tests/tests.py @@ -25,6 +25,7 @@ from django.urls import resolve, reverse from django.utils import dateformat, formats from django.utils import timezone as django_timezone from django.utils.encoding import smart_str +from django.test.utils import isolate_apps from django.utils.translation import gettext_lazy as _ from test_app.fixtures.custom_get_cid import get_cid as custom_get_cid from test_app.models import ( @@ -123,6 +124,27 @@ class SimpleModelTest(TestCase): msg="The change is correctly logged", ) + @isolate_apps("auditlog_tests") + def test_deletion_with_deferred_fields_does_not_crash(db): + from auditlog.models import LogEntry + from auditlog.registry import auditlog + + class Book(models.Model): + title = models.CharField(max_length=100) + description = models.TextField() + class Meta: + app_label = "auditlog_tests" + + auditlog.register(Book, serialize_data=True) + + b = Book.objects.create(title="foo", description="bar") + b = Book.objects.defer("description").get(id=b.id) # defer a field + b.delete() + + assert LogEntry.objects.filter(object_pk=b.pk).exists() + + + def test_update_specific_field_supplied_via_save_method(self): obj = self.obj