diff --git a/CHANGELOG.md b/CHANGELOG.md index 594eaeba..66afb7e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Improvements - feat: Add `serialized_data` field on `LogEntry` model. ([#412](https://github.com/jazzband/django-auditlog/pull/412)) +- feat: Display the field name as it would be displayed in Django Admin or use `mapping_field` if available [#428](https://github.com/jazzband/django-auditlog/pull/428) #### Fixes diff --git a/auditlog/mixins.py b/auditlog/mixins.py index 2d16e1a0..1936763a 100644 --- a/auditlog/mixins.py +++ b/auditlog/mixins.py @@ -2,12 +2,15 @@ from django import urls as urlresolvers from django.conf import settings +from django.core.exceptions import FieldDoesNotExist +from django.forms.utils import pretty_name from django.urls.exceptions import NoReverseMatch from django.utils.html import format_html, format_html_join from django.utils.safestring import mark_safe from django.utils.timezone import localtime from auditlog.models import LogEntry +from auditlog.registry import auditlog MAX = 75 @@ -81,7 +84,9 @@ def msg(self, obj): msg.append("") msg.append(self._format_header("#", "Field", "From", "To")) for i, (field, change) in enumerate(sorted(atom_changes.items()), 1): - value = [i, field] + (["***", "***"] if field == "password" else change) + value = [i, self.field_verbose_name(obj, field)] + ( + ["***", "***"] if field == "password" else change + ) msg.append(self._format_line(*value)) msg.append("
") @@ -99,7 +104,7 @@ def msg(self, obj): format_html( "{}{}{}{}", i, - field, + self.field_verbose_name(obj, field), change["operation"], change_html, ) @@ -120,3 +125,19 @@ def _format_line(self, *values): return format_html( "".join(["", "{}" * len(values), ""]), *values ) + + def field_verbose_name(self, obj, field_name: str): + model = obj.content_type.model_class() + try: + model_fields = auditlog.get_model_fields(model._meta.model) + mapping_field_name = model_fields["mapping_fields"].get(field_name) + if mapping_field_name: + return mapping_field_name + except KeyError: + # Model definition in auditlog was probably removed + pass + try: + field = model._meta.get_field(field_name) + return pretty_name(getattr(field, "verbose_name", field_name)) + except FieldDoesNotExist: + return pretty_name(field_name) diff --git a/auditlog_tests/tests.py b/auditlog_tests/tests.py index 0cb1e7a8..5751f4f4 100644 --- a/auditlog_tests/tests.py +++ b/auditlog_tests/tests.py @@ -1323,8 +1323,8 @@ def test_changes_msg_delete(self): ( "" "" - "" - "" + "" + "" "
#FieldFromTo
1field onevalue before deletionNone
2field two11None
1Field onevalue before deletionNone
2Field two11None
" ), ) @@ -1346,8 +1346,8 @@ def test_changes_msg_create(self): ( "" "" - "" - "" + "" + "" "
#FieldFromTo
1field oneNonea value
2field twoNone11
1Field oneNonea value
2Field twoNone11
" ), ) @@ -1369,9 +1369,9 @@ def test_changes_msg_update(self): ( "" "" - "" + "" "" - "" + "" "
#FieldFromTo
1field oneold value of field one
1Field oneold value of field onenew value of field one
2field two1142
2Field two1142
" ), ) @@ -1394,12 +1394,38 @@ def test_changes_msg_m2m(self): ( "" "" - "" "
#RelationshipActionObjects
1some_m2m_fieldaddExample User (user 1)" + "
1Some m2m fieldaddExample User (user 1)" "
Illustration (user 42)
" ), ) + def test_unregister_after_log(self): + log_entry = self._create_log_entry( + LogEntry.Action.CREATE, + { + "field two": [None, 11], + "field one": [None, "a value"], + }, + ) + # Unregister + auditlog.unregister(SimpleModel) + self.assertEqual( + self.admin.msg_short(log_entry), "2 changes: field two, field one" + ) + self.assertEqual( + self.admin.msg(log_entry), + ( + "" + "" + "" + "" + "
#FieldFromTo
1Field oneNonea value
2Field twoNone11
" + ), + ) + # Re-register + auditlog.register(SimpleModel) + class NoDeleteHistoryTest(TestCase): def test_delete_related(self):