From 99847e5d40979a3046729ec3f3b8e29f1d953629 Mon Sep 17 00:00:00 2001 From: Tosinibikunle Date: Wed, 20 Aug 2025 15:53:04 +0100 Subject: [PATCH 1/2] 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 From aca5370b76afc49b8d932f6aef6ef2e15fdc6c53 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 14:58:43 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- auditlog/models.py | 9 +++++---- auditlog_tests/tests.py | 7 +++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/auditlog/models.py b/auditlog/models.py index 6811544..eccf6c7 100644 --- a/auditlog/models.py +++ b/auditlog/models.py @@ -240,7 +240,7 @@ class LogEntryManager(models.Manager): "fields", self._get_applicable_model_fields(instance, model_fields) ) - # safeguard: if fields explicitly set → only use them + # safeguard: if fields explicitly set → only use them if "fields" in kwargs: allowed_fields = set(kwargs["fields"]) else: @@ -248,9 +248,11 @@ class LogEntryManager(models.Manager): instance_copy = self._get_copy_with_python_typed_fields(instance) - # drop any deferred field not in allowed_fields + # 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", []): + if f.name not in allowed_fields and f.name in getattr( + instance_copy, "deferred_fields", [] + ): setattr(instance_copy, f.name, None) data = dict( @@ -263,7 +265,6 @@ 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 1ce3dfd..d7f5546 100644 --- a/auditlog_tests/tests.py +++ b/auditlog_tests/tests.py @@ -21,11 +21,11 @@ from django.db.models import JSONField, Value from django.db.models.functions import Now from django.db.models.signals import pre_save from django.test import RequestFactory, TestCase, TransactionTestCase, override_settings +from django.test.utils import isolate_apps 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 ( @@ -132,8 +132,9 @@ class SimpleModelTest(TestCase): class Book(models.Model): title = models.CharField(max_length=100) description = models.TextField() + class Meta: - app_label = "auditlog_tests" + app_label = "auditlog_tests" auditlog.register(Book, serialize_data=True) @@ -142,8 +143,6 @@ class SimpleModelTest(TestCase): b.delete() assert LogEntry.objects.filter(object_pk=b.pk).exists() - - def test_update_specific_field_supplied_via_save_method(self): obj = self.obj