From 351352aa12659afaf563b6942703cb6edba693ff Mon Sep 17 00:00:00 2001 From: Lukas Graf Date: Mon, 24 Nov 2025 19:49:39 +0100 Subject: [PATCH] Make diffing more robust for polymorphic models: When working with polymorphic models, where a child model inherits from a parent model, Django's pre_save signal may send model instances in a way where the log_update() handler receives an instance of the child as the `old` model, but an instance of the parent as the `new` model. This leads to a `FieldDoesNotExist` error when a field that only exists on the child was modified, and `get_field_value()` attempts look up that field on the parent. This change makes diffing polymorphic models more robust by considering this case in `get_default_value()`. Changes to those child fields won't be tracked in these cases, but at least `django-auditlog` won't prevent the model from being saved. --- CHANGELOG.md | 4 ++++ auditlog/diff.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fead883..8923781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Add `AUDITLOG_USE_BASE_MANAGER` setting to override default manager use ([#766](https://github.com/jazzband/django-auditlog/pull/766)) - Drop 'Python 3.9' support ([#773](https://github.com/jazzband/django-auditlog/pull/773)) +#### Fixes + +- Make diffing more robust for polymorphic models ([#784](https://github.com/jazzband/django-auditlog/pull/784)) + ## 3.3.0 (2025-09-18) #### Improvements diff --git a/auditlog/diff.py b/auditlog/diff.py index 0d69749..dd64d20 100644 --- a/auditlog/diff.py +++ b/auditlog/diff.py @@ -3,7 +3,7 @@ from collections.abc import Callable from datetime import timezone from django.conf import settings -from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist from django.db.models import NOT_PROVIDED, DateTimeField, ForeignKey, JSONField, Model from django.utils import timezone as django_timezone from django.utils.encoding import smart_str @@ -74,7 +74,7 @@ def get_field_value(obj, field, use_json_for_changes=False): try: model_field = obj._meta.get_field(field.name) default = model_field.default - except AttributeError: + except (AttributeError, FieldDoesNotExist): default = NOT_PROVIDED if default is NOT_PROVIDED: