diff --git a/CHANGELOG.md b/CHANGELOG.md index d72444b..b6e5233 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ #### Fixes -- Fixed problem when setting `django.db.models.functions.Now()` in `DateTimeField` ([#635](https://github.com/jazzband/django-auditlog/pull/635)) +- Fixed a problem when setting `Value(None)` in `JSONField` ([#646](https://github.com/jazzband/django-auditlog/pull/646)) +- Fixed a problem when setting `django.db.models.functions.Now()` in `DateTimeField` ([#635](https://github.com/jazzband/django-auditlog/pull/635)) ## 3.0.0 (2024-04-12) diff --git a/auditlog/diff.py b/auditlog/diff.py index 0212e9f..2fd44a9 100644 --- a/auditlog/diff.py +++ b/auditlog/diff.py @@ -79,7 +79,10 @@ def get_field_value(obj, field): value = django_timezone.make_naive(value, timezone=timezone.utc) elif isinstance(field, JSONField): value = field.to_python(getattr(obj, field.name, None)) - value = json.dumps(value, sort_keys=True, cls=field.encoder) + try: + value = json.dumps(value, sort_keys=True, cls=field.encoder) + except TypeError: + pass elif (field.one_to_one or field.many_to_one) and hasattr(field, "rel_class"): value = smart_str( getattr(obj, field.get_attname(), None), strings_only=True diff --git a/auditlog_tests/models.py b/auditlog_tests/models.py index f5db94e..2492c6b 100644 --- a/auditlog_tests/models.py +++ b/auditlog_tests/models.py @@ -269,6 +269,12 @@ class JSONModel(models.Model): history = AuditlogHistoryField(delete_related=False) +class NullableJSONModel(models.Model): + json = models.JSONField(null=True, blank=True) + + history = AuditlogHistoryField(delete_related=False) + + class SerializeThisModel(models.Model): label = models.CharField(max_length=24, unique=True) timestamp = models.DateTimeField() @@ -346,6 +352,7 @@ auditlog.register(CharfieldTextfieldModel) auditlog.register(PostgresArrayFieldModel) auditlog.register(NoDeleteHistoryModel) auditlog.register(JSONModel) +auditlog.register(NullableJSONModel) auditlog.register( SerializeThisModel, serialize_data=True, diff --git a/auditlog_tests/tests.py b/auditlog_tests/tests.py index 0a682c6..2e853d4 100644 --- a/auditlog_tests/tests.py +++ b/auditlog_tests/tests.py @@ -17,6 +17,7 @@ from django.contrib.auth.models import AnonymousUser, User from django.contrib.contenttypes.models import ContentType from django.core import management from django.db import models +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, override_settings @@ -46,6 +47,7 @@ from auditlog_tests.models import ( ManyRelatedModel, ManyRelatedOtherModel, NoDeleteHistoryModel, + NullableJSONModel, PostgresArrayFieldModel, ProxyModel, RelatedModel, @@ -1090,6 +1092,14 @@ class DateTimeFieldModelTest(TestCase): dtm.save() self.assertEqual(dtm.naive_dt, Now()) + def test_json_field_value_none(self): + json_model = NullableJSONModel(json=Value(None, JSONField())) + json_model.save() + self.assertEqual(json_model.history.count(), 1) + self.assertEqual( + json_model.history.latest().changes_dict["json"][1], "Value(None)" + ) + class UnregisterTest(TestCase): def setUp(self): @@ -1196,7 +1206,7 @@ class RegisterModelSettingsTest(TestCase): self.assertTrue(self.test_auditlog.contains(SimpleExcludeModel)) self.assertTrue(self.test_auditlog.contains(ChoicesFieldModel)) - self.assertEqual(len(self.test_auditlog.get_models()), 25) + self.assertEqual(len(self.test_auditlog.get_models()), 26) def test_register_models_register_model_with_attrs(self): self.test_auditlog._register_models(