Login

View all log entries in the admin

Author:
jakub
Posted:
July 9, 2011
Language:
Python
Version:
1.3
Score:
4 (after 4 ratings)

By default, you can only see the action log ("History") for particular model instances and a list of your own actions on the admin's index. This adds a fully-fledged admin view for the LogEntry model, where you can filter actions by user, content type, action type, browse by change date, and also search in the change message.

Add the code to any of your apps' admin.py. The entries will be visible only to superusers and won't be editable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from django.contrib.admin.models import LogEntry, DELETION
from django.utils.html import escape
from django.core.urlresolvers import reverse


class LogEntryAdmin(admin.ModelAdmin):

    date_hierarchy = 'action_time'

    readonly_fields = LogEntry._meta.get_all_field_names()

    list_filter = [
        'user',
        'content_type',
        'action_flag'
    ]

    search_fields = [
        'object_repr',
        'change_message'
    ]


    list_display = [
        'action_time',
        'user',
        'content_type',
        'object_link',
        'action_flag',
        'change_message',
    ]

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return request.user.is_superuser and request.method != 'POST'

    def has_delete_permission(self, request, obj=None):
        return False

    def object_link(self, obj):
        if obj.action_flag == DELETION:
            link = escape(obj.object_repr)
        else:
            ct = obj.content_type
            link = u'<a href="%s">%s</a>' % (
                reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=[obj.object_id]),
                escape(obj.object_repr),
            )
        return link
    object_link.allow_tags = True
    object_link.admin_order_field = 'object_repr'
    object_link.short_description = u'object'
    
    def queryset(self, request):
        return super(LogEntryAdmin, self).queryset(request) \
            .prefetch_related('content_type')


admin.site.register(LogEntry, LogEntryAdmin)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

djibril (on July 11, 2011):

Useful snippet! Thanks I think some small things are needed:

  1. in imports from django.core.urlresolvers import reverse

  2. change : object_link_field.allow_tags = True object_link_field.admin_order_field = 'object_repr' object_link_field.short_description = u'object'

to object_link.allow_tags = True object_link.admin_order_field = 'object_repr' object_link.short_description = u'object'

#

jakub (on July 12, 2011):

Thanks for pointing out the errors. It was copied from my project and I made some last changes introducing the errors here in the form. I've updated the snippet so it should work out of the box now.

#

Zorpix (on August 6, 2013):

When I put this in my app, it gave me a reverse error. To fix it, I had to change:

else: ct = obj.content_type link = u'%s' % ( reverse('admin:%s_%s_change' % (ct.app_label, ct.model), args=[obj.object_id]), escape(obj.object_repr), ) return link

to: else: ct = obj.content_type return u'

#

rwmcfa1 (on August 19, 2013):

i would suggest adding an override for the queryset method to avoid a very large number of queries for content_type.

def queryset(self, request):
    return super(LogEntryAdmin, self).queryset(request) \
        .prefetch_related('content_type')

for me that reduces the number of queries from ~100 to ~8.

#

tgandor (on December 11, 2013):

The comment by Zorpix was cut out by the "HTML-filtering" ('t was just code... should just HTML-escape it, right?)

Anyway, I used this snippet to much enjoyment, but also found issues:

  1. Crashes on non-reversible admin URLs (e.g. removed objects, or objects which are not registered in the admin site)
  2. Filtering by users - my site has over 600 users, and only 10-20 staff. The list_filter on all users was totally impractical, and scrolled over many screens.

I made a few tweaks to get rid of these problems, and came up with this:

https://djangosnippets.org/snippets/3009/

#

cb109 (on May 24, 2024):

A small fix, the import of reverse is now: from django.urls import reverse

#

Please login first before commenting.