@@ -158,3 +158,97 @@ def djng_locale_script(context, default_language='en'):
158158 language = default_language
159159~~ return format_html(' angular-locale_{} .js' , language.lower())
160160```
161+
162+
163+ ## Example 2 from django-auditlog
164+ [ Auditlog] ( https://github.com/jjkester/django-auditlog )
165+ ([ project documentation] ( https://django-auditlog.readthedocs.io/en/latest/ ) )
166+ is a [ Django] ( /django.html ) app that logs changes to Python objects,
167+ similar to the Django admin's logs but with more details and
168+ output formats.
169+
170+ [ ** django-auditlog / src / auditlog / mixins.py** ] ( https://github.com/jjkester/django-auditlog/blob/master/src/auditlog/mixins.py )
171+
172+ ``` python
173+ import json
174+
175+ from django.conf import settings
176+ try :
177+ from django.core import urlresolvers
178+ except ImportError :
179+ from django import urls as urlresolvers
180+ try :
181+ from django.urls.exceptions import NoReverseMatch
182+ except ImportError :
183+ from django.core.urlresolvers import NoReverseMatch
184+ ~~ from django.utils.html import format_html
185+ from django.utils.safestring import mark_safe
186+
187+ MAX = 75
188+
189+
190+ class LogEntryAdminMixin (object ):
191+
192+ def created (self , obj ):
193+ return obj.timestamp.strftime(' %Y-%m-%d %H:%M:%S' )
194+ created.short_description = ' Created'
195+
196+ def user_url (self , obj ):
197+ if obj.actor:
198+ app_label, model = settings.AUTH_USER_MODEL .split(' .' )
199+ viewname = ' admin:%s _%s _change' % (app_label,
200+ model.lower())
201+ try :
202+ link = urlresolvers.reverse(viewname,
203+ args = [obj.actor.id])
204+ except NoReverseMatch:
205+ return u ' %s ' % (obj.actor)
206+ ~~ return format_html(u ' <a href="{} ">{} </a>' ,
207+ ~~ link, obj.actor)
208+
209+ return ' system'
210+ user_url.short_description = ' User'
211+
212+ def resource_url (self , obj ):
213+ app_label, model = obj.content_type.app_label,
214+ obj.content_type.model
215+ viewname = ' admin:%s _%s _change' % (app_label, model)
216+ try :
217+ args = [obj.object_pk] if obj.object_id is \
218+ None else [obj.object_id]
219+ link = urlresolvers.reverse(viewname, args = args)
220+ except NoReverseMatch:
221+ return obj.object_repr
222+ else :
223+ ~~ return format_html(u ' <a href="{} ">{} </a>' ,
224+ ~~ link, obj.object_repr)
225+ resource_url.short_description = ' Resource'
226+
227+ def msg_short (self , obj ):
228+ if obj.action == 2 :
229+ return ' ' # delete
230+ changes = json.loads(obj.changes)
231+ s = ' ' if len (changes) == 1 else ' s'
232+ fields = ' , ' .join(changes.keys())
233+ if len (fields) > MAX :
234+ i = fields.rfind(' ' , 0 , MAX )
235+ fields = fields[:i] + ' ..'
236+ return ' %d change%s : %s ' % (len (changes), s, fields)
237+ msg_short.short_description = ' Changes'
238+
239+ def msg (self , obj ):
240+ if obj.action == 2 :
241+ return ' ' # delete
242+ changes = json.loads(obj.changes)
243+ msg = ' <table><tr><th>#</th><th>Field</th>' + \
244+ ' <th>From</th><th>To</th></tr>'
245+ for i, field in enumerate (sorted (changes), 1 ):
246+ value = [i, field] + ([' ***' , ' ***' ] if field == \
247+ ' password' else changes[field])
248+ ~~ msg += format_html(' <tr><td>{} </td><td>{} </td><td>' + \
249+ ~~ ' {} </td><td>{} </td></tr>' , * value)
250+
251+ msg += ' </table>'
252+ return mark_safe(msg)
253+ msg.short_description = ' Changes'
254+ ```
0 commit comments