If you do things with the Django ORM and want an audit trails of all changes you have two options:
-
Insert some cleverness into a
pre_save
signal that writes down all changes some way. -
Use eventlog and manually log things in your views.
(you have other options too but I'm trying to make a point here)
eventlog
is almost embarrassingly simple. It's basically just a model with three fields:
- User
- An action string
- A JSON dump field
You use it like this:
from eventlog.models import log
def someview(request):
if request.method == 'POST':
form = SomeModelForm(request.POST)
if form.is_valid():
new_thing = form.save()
log(request.user, 'mymodel.create', {
'id': new_thing.id,
'name': new_thing.name,
# You can put anything JSON
# compatible in here
})
return redirect('someotherview')
else:
form = SomeModelForm()
return render(request, 'view.html', {'form': form})
That's all it does. You then have to do something with it. Suppose you have an admin page that only privileged users can see. You can make a simple table/dashboard with these like this:
from eventlog.models import Log # Log the model, not log the function
def all_events(request):
all = Log.objects.all()
return render(request, 'all_events.html', {'all': all})
And something like this to to all_events.html
:
<table>
<tr>
<th>Who</th><th>When</th><th>What</th><th>Details</th>
</tr>
{% for event in all %}
<tr>
<td>{{ event.user.username }}</td>
<td>{{ event.timestamp | date:"D d M Y" }}</td>
<td>{{ event.action }}</td>
<td>{{ event.extra }}</td>
</tr>
{% endfor %}
</table>
What I like about it is that it's very deliberate. By putting it into views at very specific points you're making it an audit log of actions, not of data changes.
Projects with overly complex model save signals tend to dig themselves into holes that make things slow and complicated. And it's not unrealistic that you'll then record events that aren't particularly important to review. For example, a cron job that increments a little value or something. It's more interesting to see what humans have done.
I just wanted to thank the Eldarion guys for eventlog. It's beautifully simple and works perfectly for me.
Comments
We started with something like this, but ended up not liking the DB bloat that this sort of thing causes. We also started finding that we were using our event log data for some light analytics, which isn't something we had anticipated.
After pondering a number of alternative methods (breaking the event log out into its own DB was the leading candidate), we discovered Keen.io. You throw event logs at it and you can do all kinds of filtering/graphing/statistical calculations based on the key/value pairs within each body.
I don't work for Keen.io, but we really love it at Pathwright. Avoids a lot of unnecessary DB writes and DB bloat, plus our less technical business staff can still figure out how to query it without involving a developer.
Keen.io looks awesome! I just spent a coupla' minutes skimming their docs and stuff.
However, even though it uses a "persistent connection" it's still a HTTPS POST over the network every time it needs to send something. Blocking too. Perhaps it's doable to "up" that to be a gevent greenlet or something so it can send async but that's probably not trivial.
As far as bloat is concerned, I think this is where it matters. I use my eventlog only to send events when users do something that relates to changing the state (e.g. doing a POST) so there aren't that many events actually.
with postgres you could do this entirely inside the db server. See http://gmod.org/wiki/Chado_Audit_Module for example sql. No code in django needed at all
But then you'd get every insert or update or something, right?
That's no explicit and not very deliberate.