django-changesets¶

Warning
This app is very much a work in progress, and far from complete or stable. This documentation is currently just a roadmap and sections of it have not yet been implemented. Once the documentation matches the code this notice will be removed.
If anyone would like to contribute to the code, fork it and create a pull request.
django-changesets is a history tracking app for Django.
Contents¶
Configuration¶
django-changesets requires the auth module, so the first configuration
step is to add the necessary apps to INSTALLED_APPS
:
INSTALLED_APPS = (
...
'django.contrib.auth',
'changesets',
...
)
Optionally, install the middleware after the auth middleware (see Middleware for details):
MIDDLEWARE_CLASSES = (
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'changesets.middleware.ChangeSetMiddleware',
...
)
If the models already contain data, then bring create an initial changeset by running:
with record_changeset(comment='Initial data'):
scan('myapp')
Usage¶
This app aims be provide a flexible and stable method of recording changes
to data by encapsulating a group of changes into a ChangeSet
, stamped
with the time and user who made the changes.
One of the more powerful features provided is the ability of record a changeset after the fact (this also makes it very easy to add the app to existing data). Since every change is simply a database record, the history is also mutable, although changes to the history need to be done with care since they could result in invalid data (e.g. broken relationships).
This is designed to work with all relationship fields, including ForeignKey
,
ManyToManyField
and GenericForeignKey
.
Model Overview¶
The primary model in this app is ChangeSet
. This represents a collection
of individual changes made at a specific point in time by a single user.
Individual changes are recorded by the object, field, and value changed.
To ensure stability of the changesets, changed model instances are not
referenced directly, instead they are wrapped in an ObjectWrapper
model.
The reason for this is that records in this model are never deleted, meaning
that changes relating to deleted objects can be kept and still be sensibly
queried.
Fields are represented by FieldType
, which is a bit like a ContentType
,
but for fields instead of models.
Changed values are converted to text and stored in a TextField
.
Tracking Changes¶
The most basic method of recording a changeset is through
record_changeset
. For example:
with record_changeset(user=my_user, comment='Some changes'):
# Change a value
obj.field = 2
obj.save()
# Add something
MyModel.objects.create(value='new object')
# Delete something
old_obj.delete()
# For a bulk operation we need to find the changes are they are made
queryset.update(my_value='new value')
scan(queryset)
Middleware¶
Often, changesets will be wanted for all changes made by a user through
a view, and this can be implemented simply by adding ChangeSetMiddleware
below AuthenticationMiddleware
in MIDDLEWARE_CLASSES
:
MIDDLEWARE_CLASSES = (
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'changesets.middleware.ChangeSetMiddleware',
...
)
This has the effect of wrapping every view in a ChangeSet and automatically assigning the current user. Note that bulk operations must still be dealt with explicitly.
Introspection¶
If changes were made and not recorded (often because of a bulk operation, or
after setting up changesets on an existing project), they can be pulled
into a changeset afterwards using scan
. For example:
with record_changeset(comment='Bulk operations just happened'):
# We know which objects were only added and changed
scan(changed_queryset)
# We know that there were only deletions here
scan(OtherModel, delete_only=True)
# Lot of stuff happened in this model
scan(MessyModel)
# This entire app has changes
scan('myapp')
Querying History¶
ChangeSets
are just models, so they can be queried just the
same as any other model.