django-auditlog icon indicating copy to clipboard operation
django-auditlog copied to clipboard

Support for changes in related models

Open jjkester opened this issue 10 years ago • 3 comments

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).

jjkester avatar Feb 16 '15 16:02 jjkester

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!

jjkester avatar May 15 '15 11:05 jjkester

Moved to 0.5.0 release to quickly ship improvements for Django 1.10.

jjkester avatar Aug 02 '16 15:08 jjkester

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'

rrauenza avatar Jul 01 '20 15:07 rrauenza