A complete solution for creating automatic history of Django models.
Project description
Django Wicked Historian
A complete solution for creating automatic history of Django models.
Installation
Package can be installed using PyPi:
$ pip install django-wicked-historian
You can also use extras to ensure some additional dependencies specific for implementation of JSONField which is required for this package to work properly.
$ pip install django-wicked-historian[mysql]
$ pip install django-wicked-historian[postgres]
$ pip install django-wicked-historian[django-jsonfield]
Defining JSONField to be used
Package requires some configuration. You need to specify JSONField implementation which package gonna use to store values of model fields in your settings:
from wicked_historian.encoder import JSONEncoder
WICKED_HISTORIAN_JSON_FIELD_CLASS = 'path.to.JSONField'
WICKED_HISTORIAN_JSON_FIELD_KWARGS = {
'encoder': JSONEncoder,
}
WICKED_HISTORIAN_JSON_FIELD_CLASS
- path to JSON field class to be used
WICKED_HISTORIAN_JSON_FIELD_KWARGS
- kwargs used for instantiate of supplied class
Remember to always use wicked_historian.encoder.JSONEncoder
as an encoder for this field.
Example configuration for common used fields
jsonfield.JSONField
WICKED_HISTORIAN_JSON_FIELD_CLASS = 'jsonfield.JSONField'
WICKED_HISTORIAN_JSON_FIELD_KWARGS = {
'encoder_class': JSONEncoder,
}
django.contrib.postgres.fields.JSONField
For Django >= 2.1 use builtin field with our encoder.
WICKED_HISTORIAN_JSON_FIELD_CLASS = 'django.contrib.postgres.fields.JSONField'
WICKED_HISTORIAN_JSON_FIELD_KWARGS = {
'encoder': JSONEncoder,
}
django_mysql.models.fields.JSONField
This field in version 2.2.0 of django-mysql package doesn't support supplying custom json encoder. However this package supplies subclass of this field with support of custom encoders and wicked_historian.encoder.JSONEncoder
is default encoder.
Use field wicked_historian.compat.mysql.JSONField
instead:
WICKED_HISTORIAN_JSON_FIELD_CLASS = 'wicked_historian.compat.mysql.JSONField'
WICKED_HISTORIAN_JSON_FIELD_KWARGS = {}
Adding history to model of choice
Model for which history is going to be generated should inherit from wicked_historian.models.DiffableHistoryModel
and have a class for history entries specified in the Model.Meta class. History class should be created using factory wicked_historian.utils.generate_history_class
:
from wicked_historian.models import DiffableHistoryModel
from wicked_historian.utils import generate_history_class
class Book(DiffableHistoryModel):
title = models.CharField(max_length=100)
class Meta:
history_class = 'this_app.models.BookEditHistory'
BookEditHistory = generate_history_class(Book, __name__)
If there is a need for customizing the history model, it can be generated with the abstract
option and used as a base model for a custom history model.
class BookEditHistory(generate_history_class(Book, __name__, abstract=True)):
custom_field = models.IntegerField(default=0)
def custom_method(self):
return self.custom_field + 10
Changes in model's fields set
If the set of model fields changes in a non-incremental way (fields were removed or changed their type), old definitions of such fields should be supplied to the generate_history_class
factory for handling already existing history entries concerning these fields:
from wicked_historian.utils import ObsoleteFieldDescription
BookEditHistory = generate_history_class(
Book,
__name__,
obsolete_field_choices=[
ObsoleteFieldDescription('title', models.TextField()),
ObsoleteFieldDescription('number_of_pages', models.IntegerField()),
ObsoleteFieldDescription('age', models.IntegerField(choices=[
(1, 'XV'),
(2, 'XIX'),
(3, 'XX'),
])),
],
)
Excluding fields from history
If there is no need for generating history for some fields, they can be excluded by supplying list of unwanted fields name to generate_history_class
. History for these will not be generated, but any already existing history can be read.
from wicked_historian.utils import ObsoleteFieldDescription
BookEditHistory = generate_history_class(
Book,
__name__,
excluded_fields=['title'],
)
Choices in model fields
Please note that when the set of choices in model fields changes in a non-incremental way, some values may be impossible to restore from history entries. That's why you should always have a superset of all choices ever used in this fields declared in the field.
Reading/accessing history
Instance history should be accessed by history model.
Retrieving whole history
To retrieve whole history use method get_for
, e.g. BookEditHistory.get_for(book)
will return list of whole Book
instance history as dicts.
Filtering and searching history
To filter history use history model manager (e.g. BookEditHistory.objects.filter(user=some_user, model=book)
) and transform history entry to dict form using get_entry_for
method.
history_entry_instances = BookEditHistory.objects.filter(user=some_user, model=book)
history_entries = BookEditHistory.get_history_entry(history_entry_instance) for history_entry_instance in history_entry_instances
Troubleshooting custom m2m handling
When there is risk of sending by Django both signals model related (pre_save, post_delete etc.) and m2m related use wicked_historian.signals_exclusion.signal_exclusion
and make those changes in signal_exclusion.model_signals_exclusion_context
context. When in context calling signal_exclusion.are_model_signals_excluded
with the same arguments context was created returns True
.
License
The Django Wicked Historian package is licensed under the FreeBSD License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file django-wicked-historian-1.0.0.tar.gz
.
File metadata
- Download URL: django-wicked-historian-1.0.0.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.11.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3dac0c456b13f42eec906ab5a73127df1ec394309dba5b412cd429c60fe3e4fc |
|
MD5 | 71fccd123fe51dcfe7b6ffadba62e88c |
|
BLAKE2b-256 | 312315cbc79ed95ffc757e9a652815ccaabaa1340902723e6507778b843650c3 |
File details
Details for the file django_wicked_historian-1.0.0-py3-none-any.whl
.
File metadata
- Download URL: django_wicked_historian-1.0.0-py3-none-any.whl
- Upload date:
- Size: 22.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.11.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.4 CPython/3.9.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | e61c377f4b3023d2d7481b4b1c39176eaf854efae8a246dbe0987f1793f8ec9d |
|
MD5 | db25966cdecbb9f65159bf70dbd49ed2 |
|
BLAKE2b-256 | 949c06a638ddb8466343fc13ee93ebd0b729f3480dc30e7ec7c36b4fef6e3524 |