Tying in to Django Admin's Model History

The admin history is just an app like any other Django app, with the exception being special placement on the admin site.

The model is in django.contrib.admin.models.LogEntry.

When a user makes a change, add to the log like this (stolen shamelessly from contrib/admin/options.py:

from django.utils.encoding import force_unicode
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.models import LogEntry, ADDITION
LogEntry.objects.log_action(
    user_id         = request.user.pk, 
    content_type_id = ContentType.objects.get_for_model(object).pk,
    object_id       = object.pk,
    object_repr     = force_unicode(object), 
    action_flag     = ADDITION
)

where object is the object that was changed of course.

Now I see Daniel's answer and agree with him, it is pretty limited.

In my opinion a stronger approach is to use the code from Marty Alchin in his book Pro Django (see Keeping Historical Records starting at page 263). There is an application django-simple-history which implements and extends this approach (docs here).


The admin's change history log is defined in django.contrib.admin.models, and there's a history_view method in the standard ModelAdmin class.

They're not particularly clever though, and fairly tightly coupled to the admin, so you may be best just using these for ideas and creating your own version for your app.


I know this question is old, but as of today (Django 1.9), Django's history items are more robust than they were at the date of this question. In a current project, I needed to get the recent history items and put them into a dropdown from the navbar. This is how I did it and was very straight forward:

*views.py*    

from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION

def main(request, template):

    logs = LogEntry.objects.exclude(change_message="No fields changed.").order_by('-action_time')[:20]
    logCount = LogEntry.objects.exclude(change_message="No fields changed.").order_by('-action_time')[:20].count()

    return render(request, template, {"logs":logs, "logCount":logCount})

As seen in the above code snippet, I'm creating a basic queryset from the LogEntry model (django.contrib.admin.models.py is where it's located in django 1.9) and excluding the items where no changes are involved, ordering it by the action time and only showing the past 20 logs. I'm also getting another item with just the count. If you look at the LogEntry model, you can see the field names that Django has used in order to pull back the pieces of data that you need. For my specific case, here is what I used in my template:

Link to Image Of Final Product

*template.html*

<ul class="dropdown-menu">
    <li class="external">
        <h3><span class="bold">{{ logCount }}</span> Notification(s) </h3>
        <a href="{% url 'index' %}"> View All </a>
    </li>
        {% if logs %}
            <ul class="dropdown-menu-list scroller actionlist" data-handle-color="#637283" style="height: 250px;">
                {% for log in logs %}
                    <li>
                        <a href="javascript:;">
                            <span class="time">{{ log.action_time|date:"m/d/Y - g:ia" }} </span>
                            <span class="details">
                                {% if log.action_flag == 1 %}
                                    <span class="label label-sm label-icon label-success">
                                        <i class="fa fa-plus"></i>
                                    </span>
                                {% elif log.action_flag == 2 %}
                                    <span class="label label-sm label-icon label-info">
                                        <i class="fa fa-edit"></i>
                                    </span>
                                {% elif log.action_flag == 3 %}
                                    <span class="label label-sm label-icon label-danger">
                                        <i class="fa fa-minus"></i>
                                    </span>
                                {% endif %}
                                {{ log.content_type|capfirst }}: {{ log }}
                            </span>
                        </a>
                    </li>
                 {% endfor %}
            </ul>
        {% else %}
            <p>{% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}</p>
        {% endif %}
    </li>
</ul>