django-auditlog
django-auditlog copied to clipboard
Support for changes in related models
Currently, as discovered in https://github.com/jjkester/django-auditlog/issues/8, Auditlog does not behave well when it comes to related data.
The following needs improvement:
- Handle fixture loading (https://github.com/jjkester/django-auditlog/issues/8)
- Make sure (test) that save is triggered when a reverse one-to-one relationship is changed. For example, when a different Profile object is assigned to a user from the other side of the relationship. If not, create a workaround for this.
- Find a way to handle many-to-many relationships (easy logging for through model, easy lookup of data).
Did some research:
- Evaluating the many-to-many relation on
save()
is too expensive since the list of related objects could be massive. -
m2m_changed
signal could be used but also could introduce a lot of log entries since every mutation is logged, not every transaction. - Setting signals on the through-model of a relation could work, there only needs to be a way to tie these log entries to the related models. (Also, this could also could introduce some log spam.)
Nice detail: the last method is currently working. Take the following example:
class MyModel(models.Model):
related = models.ManyToManyField('MySecondaryModel')
auditlog.register(MyModel, exclude_fields['related']) # At this point unsure whether the exclusion is mandatory
auditlog.register(MyModel.related.through)
# Get related log entries
obj = MyModel.objects.first()
rel_pks = obj.related.all().values_list('id', flat=True)
related_logentries = LogEntry.objects.get_for_model(MyModel.related.through).filter(object_id__in=rel_pks)
Parts of this example can be reworked into utility methods to make using them cleaner and more straightforward.
Ideas? Did I miss something? Please let me know!
Moved to 0.5.0 release to quickly ship improvements for Django 1.10.
Found another case I think of the fixture loading not working:
Traceback (most recent call last):
virtualenv-3.6.8/lib/python3.6/site-packages/nose/suite.py line 210 in run
self.setUp()
virtualenv-3.6.8/lib/python3.6/site-packages/nose/suite.py line 293 in setUp
self.setupContext(ancestor)
virtualenv-3.6.8/lib/python3.6/site-packages/nose/suite.py line 316 in setupContext
try_run(context, names)
virtualenv-3.6.8/lib/python3.6/site-packages/nose/util.py line 471 in try_run
return func()
perfauto/lib/testing/testcase.py line 512 in setUpClass
return super().setUpClass()
perfauto/lib/testing/testcase.py line 409 in setUpClass
return super().setUpClass()
perfauto/lib/testing/testcase.py line 394 in setUpClass
return super().setUpClass()
perfauto/lib/testing/testcase.py line 147 in setUpClass
super().setUpClass()
virtualenv-3.6.8/lib/python3.6/site-packages/django/test/testcases.py line 1131 in setUpClass
call_command('loaddata', *cls.fixtures, **{'verbosity': 0, 'database': db_name})
virtualenv-3.6.8/lib/python3.6/site-packages/django/core/management/__init__.py line 148 in call_command
return command.execute(*args, **defaults)
virtualenv-3.6.8/lib/python3.6/site-packages/django/core/management/base.py line 364 in execute
output = self.handle(*args, **options)
virtualenv-3.6.8/lib/python3.6/site-packages/django_nose/runner.py line 331 in _foreign_key_ignoring_handle
_old_handle(self, *fixture_labels, **options)
virtualenv-3.6.8/lib/python3.6/site-packages/django/core/management/commands/loaddata.py line 72 in handle
self.loaddata(fixture_labels)
virtualenv-3.6.8/lib/python3.6/site-packages/django/core/management/commands/loaddata.py line 114 in loaddata
self.load_label(fixture_label)
virtualenv-3.6.8/lib/python3.6/site-packages/django/core/management/commands/loaddata.py line 181 in load_label
obj.save(using=self.using)
virtualenv-3.6.8/lib/python3.6/site-packages/django/core/serializers/base.py line 223 in save
models.Model.save_base(self.object, using=using, raw=True, **kwargs)
virtualenv-3.6.8/lib/python3.6/site-packages/django/db/models/base.py line 790 in save_base
update_fields=update_fields, raw=raw, using=using,
virtualenv-3.6.8/lib/python3.6/site-packages/django/dispatch/dispatcher.py line 175 in send
for receiver in self._live_receivers(sender)
virtualenv-3.6.8/lib/python3.6/site-packages/django/dispatch/dispatcher.py line 175 in <listcomp>
for receiver in self._live_receivers(sender)
virtualenv-3.6.8/lib/python3.6/site-packages/auditlog/receivers.py line 16 in log_create
changes = model_instance_diff(None, instance)
virtualenv-3.6.8/lib/python3.6/site-packages/auditlog/diff.py line 135 in model_instance_diff
new_value = get_field_value(new, field)
virtualenv-3.6.8/lib/python3.6/site-packages/auditlog/diff.py line 80 in get_field_value
value = field.default if field.default is not NOT_PROVIDED else None
AttributeError: Problem installing fixture 'data.json': 'OneToOneRel' object has no attribute 'default'