Usage

Registering Action Types

Message types must be registered with Object Log as a LogAction before they can be used. This informs Object Log about what information you will be logging and how to render it. LogAction is comprised of a key and a django template. The key will be used to identify the type quickly by developers.

1from object_log.models import LogAction
2
3LogAction.objects.register('MY_EVENT','path/to/my/template.html')

Registering a LogAction a second time will override the existing settings. This allows you to replace the template used to render an action type provided by another app.

1LogAction.objects.register('MY_EVENT','path/to/my/template.html')
2LogAction.objects.register('MY_EVENT','path/to/my/template.html')

Adding Log Entries

log_action(key, user, object1, object2=None, object3=None)

LogItem can be created manually, but the prefered method for adding log entries is to use LogItem.objects.log_action(). log_action uses an internal cache provided by LogAction to avoid queries to fetch LogAction from the database.

  • key is the key corresponding to the LogAction you are recording
  • user is the user performing the action
  • object1, object2, and object3 are the objects involved in the action. They are stored using a GenericForeignKey. At least one object is required.
 1from object_log.models import LogItem
 2
 3# store log_action for faster access
 4log = LogItem.objects.log_action
 5
 6def my_view(request, pk):
 7    """ example view that retrieves an object by its pk """ 
 8    obj = SomeModel.objects.get(pk=pk)
 9    log('MY_EVENT', request.user, obj) 

Storing Arbitrary Data

Arbitrary data can be stored with an log entry. For instance you may want to store a list of properties that were edited. Storing arbitrary data allows log_items to be rendered quickly without the need to look up related objects.

Arbitrary data is added by including the data kwarg when calling log_action. Data should be a dict containing values that are json serializable.

1def my_view(request, pk):
2    """ example view that retrieves an object by its pk """ 
3    obj = SomeModel.objects.get(pk=pk)
4    log('MY_EVENT', request.user, obj, data={'foo':obj.foo}) 

The data is serialized automatically with json and is available within templates as log_item.data

Caching Data Automatically

In addition manually specifying data, a cache function can be used to automatically build cached data. Cache functions are declared when registering the LogAction. The function is used to parse related data from a log entry.

  • The function should accept user, object1, object2, object3, and data.
  • The function should return a dict.
  • The function should be flexible enough to handle when an object is None.

Arbitrary data can still be supplied but cached data will take precedence.

 1def build_cache(user, obj1, obj2, obj3, data):
 2    return {'foo':obj.foo}
 3
 4register('MY_EVENT', 'template.html', build_cache)
 5
 6def my_view(request, pk):
 7    """ example view that retrieves an object by its pk """ 
 8    obj = SomeModel.objects.get(pk=pk)
 9    log('MY_EVENT', request.user, obj) 

If you make changes to a cache, the cache can be rebuilt using manage.py rebuild_log_cache. This is a naive rebuild which will add cached values to existing data. If cache changes in more substantial ways you will be required to create a manual migration script.

Creating Templates for Action Types

Each LogAction must have a template registered to render it.

1{# sample template for MY_EVENT #}
2 did something interesting to 

Templates tags and filters can be used like in any other django template. You may also use contextual links to improve navigation.

1<a href="{% url user-detail log_item.user_id %}">{{log_item.user.username}}</a> 
2did something interesting to 
3<a href="{% url object-detail log_item.object1_id %}">{{log_item.object1}}</a>

Timestamp should not be output in the log. The default template for rendering log entries outputs the timestamp so that it is consistent across all log types.

Related Objects That Have Been Deleted.

Log entries are not deleted with their related objects. This is a business logic decision left up to projects that use Object Log.
When related objects are deleted they are returned as a None within the template. This will result in either an Empty space, or an exception if you applied a template filter incapable of handling an None for it input.

Arbitrary Data can be used to store values that will persist beyond the deletion of an object.

Built-in Action Types

CREATE, EDIT, DELETE are registered by default since these actions occur in nearly every django app. The supplied templates are basic and do not provide contextually links to the objects.

Displaying Logs

Object Log provides some generic views for rendering a log for either User or for an Object

activate them by adding object_log.urls to your projects urls.py

1urlpatterns += patterns('',
2    (r'^', include('object_log.urls')),
3)

Displaying Actions Performed by a User

views.list_user_actions(request, pk)

accessible from /User/(?P<pk>\d+)/actions/, this view will display all actions a user has performed. It only allows access for superusers.

Display Actions Performed on a User

accessible from /User/(?P<pk>\d+)/object_log/, this view will display all actions performed on a User. It only allows access for superusers.

Display Actions Performed on a Group

accessible from /Group/(?P<pk>\d+)/object_log/, this view will display all actions performed on a Group. It only allows access for superusers.

Display Actions Performed on a Related Object

views.list_for_object(request, obj)

Object log does not expose a view that renders a log for any object. Instead object log provides a view shortcut that can be used within your application. Wrap list_for_object within your own view. list_for_object will return log entries with the object listed as object1, object2, or object3

1from object_log.views import list_for_object
2
3def list_for_my_model(request, pk):
4    """ example view using list_for_object to display a log """ 
5    obj = MyModel.objects.get(pk=pk)
6    return list_for_object(request, obj)

list_for_object performs no permission checks. It is impossible to determine the permission requirements of your project. It is up to you to implement required security.

 1from django.contrib.auth.decorators import login_required
 2from django.http import HttpResponseForbidden
 3
 4from object_log.views import list_for_object
 5
 6@login_required
 7def list_for_my_model(request, pk):
 8    """ example view using list_for_object to display a log """ 
 9    obj = MyModel.objects.get(pk=pk)
10    if request.user.has_perm('MyModel.my_perm'):
11        return list_for_object(request, obj)
12    else:
13        return HttpResponseForbidden("Insufficient permissions!")

Custom View to Display a Log

If you need more customization, such as being able to filter the list of log entries you can create a custom view. The generic views all use object_log/log.html to render a list of LogItem

1from object_log.models import LogItem
2
3def custom_log_output(request):
4    log = LogItem.objects.filter(q).distinct()
5
6    return render_to_response('object_log/log.html',
7        {'log':log},
8        context_instance=RequestContext(request))

additional context can be passed to all LogItem by included context in the Context used to render object_log/log.html

1    return render_to_response('object_log/log.html',
2        {'log':log
3         'context':{'user':request.user}
4        }, context_instance=RequestContext(request))