|
1 | 1 | import ast |
| 2 | +import contextlib |
2 | 3 | import json |
3 | 4 | from copy import deepcopy |
4 | 5 | from datetime import timezone |
5 | | -from typing import Any, Dict, List |
| 6 | +from typing import Any, Callable, Dict, List |
6 | 7 |
|
7 | 8 | from dateutil import parser |
8 | 9 | from dateutil.tz import gettz |
@@ -201,7 +202,7 @@ def _get_pk_value(self, instance): |
201 | 202 | pk_field = instance._meta.pk.name |
202 | 203 | pk = getattr(instance, pk_field, None) |
203 | 204 |
|
204 | | - # Check to make sure that we got an pk not a model object. |
| 205 | + # Check to make sure that we got a pk not a model object. |
205 | 206 | if isinstance(pk, models.Model): |
206 | 207 | pk = self._get_pk_value(pk) |
207 | 208 | return pk |
@@ -334,6 +335,7 @@ class Action: |
334 | 335 | action = models.PositiveSmallIntegerField( |
335 | 336 | choices=Action.choices, verbose_name=_("action"), db_index=True |
336 | 337 | ) |
| 338 | + changes_text = models.TextField(blank=True, verbose_name=_("change message")) |
337 | 339 | changes = models.JSONField(null=True, verbose_name=_("change message")) |
338 | 340 | actor = models.ForeignKey( |
339 | 341 | to=settings.AUTH_USER_MODEL, |
@@ -388,7 +390,7 @@ def changes_dict(self): |
388 | 390 | """ |
389 | 391 | :return: The changes recorded in this log entry as a dictionary object. |
390 | 392 | """ |
391 | | - return self.changes or {} |
| 393 | + return changes_func(self) |
392 | 394 |
|
393 | 395 | @property |
394 | 396 | def changes_str(self, colon=": ", arrow=" \u2192 ", separator="; "): |
@@ -436,7 +438,7 @@ def changes_display_dict(self): |
436 | 438 | changes_display_dict[field_name] = values |
437 | 439 | continue |
438 | 440 | values_display = [] |
439 | | - # handle choices fields and Postgres ArrayField to get human readable version |
| 441 | + # handle choices fields and Postgres ArrayField to get human-readable version |
440 | 442 | choices_dict = None |
441 | 443 | if getattr(field, "choices", []): |
442 | 444 | choices_dict = dict(field.choices) |
@@ -495,7 +497,7 @@ class AuditlogHistoryField(GenericRelation): |
495 | 497 | A subclass of py:class:`django.contrib.contenttypes.fields.GenericRelation` that sets some default |
496 | 498 | variables. This makes it easier to access Auditlog's log entries, for example in templates. |
497 | 499 |
|
498 | | - By default this field will assume that your primary keys are numeric, simply because this is the most |
| 500 | + By default, this field will assume that your primary keys are numeric, simply because this is the most |
499 | 501 | common case. However, if you have a non-integer primary key, you can simply pass ``pk_indexable=False`` |
500 | 502 | to the constructor, and Auditlog will fall back to using a non-indexed text based field for this model. |
501 | 503 |
|
@@ -532,3 +534,24 @@ def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS): |
532 | 534 | # method. However, because we don't want to delete these related |
533 | 535 | # objects, we simply return an empty list. |
534 | 536 | return [] |
| 537 | + |
| 538 | + |
| 539 | +# should I add a signal receiver for setting_changed? |
| 540 | +changes_func = None |
| 541 | + |
| 542 | + |
| 543 | +def _changes_func() -> Callable[[LogEntry], Dict]: |
| 544 | + def json_then_text(instance: LogEntry) -> Dict: |
| 545 | + if instance.changes: |
| 546 | + return instance.changes |
| 547 | + elif instance.changes_text: |
| 548 | + with contextlib.suppress(ValueError): |
| 549 | + return json.loads(instance.changes_text) |
| 550 | + return {} |
| 551 | + |
| 552 | + def default(instance: LogEntry) -> Dict: |
| 553 | + return instance.changes or {} |
| 554 | + |
| 555 | + if settings.AUDITLOG_USE_TEXT_CHANGES_IF_JSON_IS_NOT_PRESENT: |
| 556 | + return json_then_text |
| 557 | + return default |
0 commit comments