django-adminjournal¶
This library added extended capabilities to log access to Django ModelAdmins.
Features¶
- Log additions, changes, deletions of models via the Django admin
- Log read access to change lists and model instances (unsaved change views)
- Log calls to actions in changelists of ModelAdmins
Requirements¶
django-adminjournal supports Python 3 only and requires at least Django 1.11. The package uses Django’s JSONField. Therefore, PostgreSQL database backend is required.
Prepare for development¶
A Python 3.6 interpreter is required in addition to pipenv.
$ pipenv install --python 3.6 --dev
$ pipenv shell
$ pip install -e .
Now you’re ready to run the tests:
$ pipenv run py.test
Resources¶
Contents:
Installation¶
Install with pip:
pip install django-adminjournal
Your
INSTALLED_APPS
setting:INSTALLED_APPS = ( # ... 'adminjournal', )
Configuration options¶
ADMINJOURNAL_PERSISTENCE_BACKEND
defines the backend that is used to store/persist the journal entries. Default is a database backend.ADMINJOURNAL_MODEL_WHITELIST
defines the models to automatically activate the ModelAdmin mixin. The settings should be a list of Django models (e.g.auth.User
) or the string'__all__'
to activate the admin journal for all models.ADMINJOURNAL_ENTRY_EXPIRY_DAYS
defines the number of days after which the journal entries are deleted when calling the management commandclearadminjournal
. The default is365
days.
Usage¶
After adding adminjournal
to INSTALLED_APPS
, the journal is activated for
all model admins added to Django’s default AdminSite (django.contrib.admin.site
).
Cleanup¶
Journal entries can be deleted automatically after a given amount of time (see
configuration option ADMINJOURNAL_ENTRY_EXPIRY_DAYS
). To do so, run the
clearadminjournal
regulary using a cron daemon or some other trigger tool.
If you use uwsgi, you might even us the built in cron helper:
[uwsgi]
# ...
# Django session cleanup
cron = 30 4 -1 -1 -1 django-admin clearsessions
# Adminjournal cleanup
cron = 15 4 -1 -1 -1 django-admin clearadminjournal
This would run the cleanup command every day at 4:15 am.
Api documentation:
API Reference¶
adminjournal package¶
Subpackages¶
adminjournal.persistence_backends package¶
-
class
adminjournal.persistence_backends.db.
Backend
[source]¶ Bases:
adminjournal.persistence_backends.base.BaseBackend
Database-backed persistence layer for journal entries. Uses adminjournal.Entry model to store entries to database.
-
class
adminjournal.persistence_backends.log.
Backend
[source]¶ Bases:
adminjournal.persistence_backends.base.BaseBackend
Simple backend that uses Python-logging to “persist” a given entry.
- The log backend has two settings:
- ADMINJOURNAL_BACKEND_LOG_LOGGER: Name of the logger to use
- ADMINJOURNAL_BACKEND_LOG_LEVEL: Valid logging level name to use
Submodules¶
adminjournal.admin module¶
-
class
adminjournal.admin.
EntryAdmin
(model, admin_site)[source]¶ Bases:
django.contrib.admin.options.ModelAdmin
-
list_display
= ('__str__', 'action', 'content_type_repr', 'user_repr', 'object_id', 'lazy_description')[source]¶
-
readonly_fields
= ('timestamp', 'action', 'user', 'user_repr', 'content_type', 'content_type_repr', 'object_id', 'object_repr', 'description', 'payload')[source]¶
-
has_add_permission
(request, obj=None)[source]¶ has_add_permission is overwritten to ensure no entries can be added.
-
has_delete_permission
(request, obj=None)[source]¶ has_delete_permission is overwritten to ensure nobody can remove entries.
-
adminjournal.apps module¶
adminjournal.entry module¶
-
class
adminjournal.entry.
Entry
(action, user, model_class=None, model=None, description=None, timestamp=None, payload=None)[source]¶ Bases:
object
This class represents a journal entry and provides methods to get information about the action which was tracked.
-
__init__
(action, user, model_class=None, model=None, description=None, timestamp=None, payload=None)[source]¶ Create a new entry instance.
The constructor handles the provided data and does some basis validation.
The parameters action and user are always required. It is possible to override the timestamp which used for that event.
In addition, model_class and/or model needs to be provided. If both are given, the constructor will ensure that the model_class fits the provided model.
- It is allowed to provide one of the following as model_class:
- Python class of a Django model
- ContentType model instance
- None (if none is provided, the model_class will be derived from the given model).
You don’t have to provide a model if you already have the model_class on hand.
The parameter description is useful to provide a human-readable representation of what happened.
-
adminjournal.mixins module¶
-
class
adminjournal.mixins.
JournaledModelAdminMixin
[source]¶ Bases:
object
Mixin for ModelAdmin classes to issue journal entries on various actions via the model admin.
- Tracked actions:
- View changelist (w/ and w/o filters)
- View object change view
- Change object
- Add object
- Delete object
- Changelist actions (selected action and selected objects)
-
log_to_adminjournal
(action, user, message, model=None, payload=None)[source]¶ The log_to_adminjournal method requires at least the action type and the issuing user together with a human readable message or a change_message-style list from Django’s LogEntry.
The method might use change_message-style lists to generate a human readable version of the data.
If a change_message-style input is provided, the payload is ignored.
If a str message is provided and the payload is a dictionary, the data is passed to the persistence layer.
-
log_addition
(request, model, message)[source]¶ In addition to the Django LogEntry, add another entry to the adminjournal.
-
log_change
(request, model, message)[source]¶ In addition to the Django LogEntry, add another entry to the adminjournal.
-
log_deletion
(request, model, object_repr)[source]¶ In addition to the Django LogEntry, add another entry to the adminjournal.
-
render_change_form
(request, *args, **kwargs)[source]¶ If a object change view is requested (GET request on change view), a ACTION_VIEW entry is generated to track read access to single objects.
-
response_action
(request, queryset)[source]¶ Actions on change lists are tracked too. To achieve this, this method parses the POST request and checks for various action fields to
- Learn what action was triggered
- What obejects are included in the action call
- Are all objects involved or just some selected ones
We don’t know what the action does, therefore all actions are tracked as ACTION_VIEW.
-
changelist_view
(request, *args, **kwargs)[source]¶ GET requests on the changelist are tracked as ACTION_VIEW entries. The changelist view might also return redirects. This case is handled by ensuring that a proper TemplateResponse is available and the “cl” context (which is the ChangeList instance) is present.
Parameters passed to the change list are tracked too. This allows to reconstruct the subset of objects a user viewed.
adminjournal.models module¶
-
class
adminjournal.models.
Entry
(id, timestamp, action, user, user_repr, content_type, content_type_repr, object_id, description, payload)[source]¶ Bases:
django.db.models.base.Model
-
timestamp
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
action
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
user
[source]¶ Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parent
is aForwardManyToOneDescriptor
instance.
-
user_repr
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
content_type
[source]¶ Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parent
is aForwardManyToOneDescriptor
instance.
-
content_type_repr
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
object_id
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
description
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
payload
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
content_type_id
[source]¶ A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
-
get_next_by_timestamp
(*, field=<django.db.models.fields.DateTimeField: timestamp>, is_next=True, **kwargs)[source]¶
-
get_previous_by_timestamp
(*, field=<django.db.models.fields.DateTimeField: timestamp>, is_next=False, **kwargs)[source]¶
-
adminjournal.monkeypatch module¶
adminjournal.persistence module¶
-
adminjournal.persistence.
persist
(entry, backend=None)[source]¶ The persist function is the abstract entrypoint to persist journal entries. The method receives a adminjournal.entry.Entry instance and an optional backend parameter to override the default persistence backend.
The return value is either True of False, to signal if the entry was saved.