8

When I inherit from admin.ModelAdmin, in history on admin page I can see what fields has been changed. However, now I need to use django-simple-history to track all my model changes. Now, for admin, I inherit for simple_history.SimpleHistoryAdmin. Whilst I can see all of the model changes and revert them, I cannot see, which fields were changed. Is it possible to add that handy functionality to SimpleHistoryAdmin?

Anil Panda
  • 375
  • 1
  • 3
  • 14

6 Answers6

6

I found a way to solve this issue. I added a ModelAdmin method and used History Diffing to add a custom field in the Change history table.

history_list_display = ['changed_fields']

def changed_fields(self, obj):
    if obj.prev_record:
        delta = obj.diff_against(obj.prev_record)
        return delta.changed_fields
    return None
Rafi
  • 467
  • 6
  • 17
5

What you need is history_list_display field in your Admin. The list of fields included in the history_list_display will be displayed in the history page with their corresponding entries.

Something like this:

class SomeAdmin(admin.ModelAdmin):

    def some_user_defined(self, obj):
        return "something"

    date_hierarchy = 'created_at'
    search_fields = ['field1', 'field2']
    list_display = ('field1', 'field2',)
    list_filter = ('field1',)
    history_list_display = ('field1', 'field2', 'some_user_defined',)

This will display field1, field2 along with comment, user and reason

Pramod Solanky
  • 1,690
  • 15
  • 17
4

You probably want to do something like that:

# admin.py

from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin
from .models import Website
from django.utils.html import format_html


class WebsiteHistoryAdmin(SimpleHistoryAdmin):
    history_list_display = ["changed_fields","list_changes"]
    
    def changed_fields(self, obj):
        if obj.prev_record:
            delta = obj.diff_against(obj.prev_record)
            return delta.changed_fields
        return None

    def list_changes(self, obj):
        fields = ""
        if obj.prev_record:
            delta = obj.diff_against(obj.prev_record)

            for change in delta.changes:
                fields += str("<strong>{}</strong> changed from <span style='background-color:#ffb5ad'>{}</span> to <span style='background-color:#b3f7ab'>{}</span> . <br/>".format(change.field, change.old, change.new))
            return format_html(fields)
        return None


admin.site.register(Website, WebsiteHistoryAdmin)

And you get this as a result:

enter image description here

M.C
  • 359
  • 4
  • 7
1

And if you want to view not only names of changed fields as per Rafi comment and also changed values, next code will do it:

def changed_fields_with_values(self, obj):
    fields = ""
    if obj.prev_record:
        delta = obj.diff_against(obj.prev_record)

        for change in delta.changes:
            fields += str("{} changed from {} to {}".format(change.field, change.old, change.new))
        return fields
    return None
schum
  • 56
  • 1
  • 3
1

Similar to the previous solution from Rafi but using array to list more elegantly the record changes:

def list_changes(self, obj):
    diff = []
    if obj.prev_record:
        delta = obj.diff_against(obj.prev_record)

        for change in delta.changes:
            diff.append("<b>* {}:</b> changed from `{}` to `{}`".format(change.field, change.old, change.new))

    return mark_safe("\n<br>".join(diff))
areski
  • 291
  • 2
  • 6
0

Adding on to what @Rafi suggested, I created a class for this that I can use as a mixin:

class EnhancedSimpleHistoryAdmin(SimpleHistoryAdmin):

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if hasattr(cls, 'history_list_display'):
            cls.history_list_display.append('changed_fields')
        else:
            cls.history_list_display = ['changed_fields']

    def changed_fields(self, obj):
        if obj.prev_record:
            delta = obj.diff_against(obj.prev_record)
            return ", ".join(delta.changed_fields)
        return None
Dovmo
  • 8,121
  • 3
  • 30
  • 44