djangodjango-reversion

How to restore related objects with django-reversion


How can I restore related objects by django-reversion.

I tried the following which does not work...

models.py:

@reversion.register(follow=['bar_set',])
class Foo(models.Model):
    def save(self, *args, **kwargs):
        with transaction.atomic(), reversion.create_revision():
            return super(Foo, self).save(*args, **kwargs)

@reversion.register
class Bar(models.Model):
    foo = models.ForeignKey(Foo)

    def save(self, *args, **kwargs):
        with transaction.atomic(), reversion.create_revision():
            return super(Bar, self).save(*args, **kwargs)

And the following test

test.py:

class TestModels(TestCase):
    def test_soft_delete(self):
        from myapp.models import Foo, Bar
        a = Foo()
        a.save()

        b1 = Bar(foo=a)
        b1.save()
        b2 = Bar(foo=a)
        b2.save()

        a.delete()

        self.assertEqual(0, Foo.objects.count())
        self.assertEqual(0, Bar.objects.count())

        version = reversion.get_deleted(Foo)[0]  # There is only one.
        version.revert()

        self.assertEqual(1, Foo.objects.count())
        self.assertEqual(2, Bar.objects.count()) # HERE IT FAILS: 2 != 0

Solution

  • Finally I was able to do it manually in some suboptimal way:

    I have putted the code into a custom QuerySet

    class FooQuerySet(models.QuerySet):
        def restore(self, id):
            # restore
            deleted_list = reversion.get_deleted(self.model)
            deleted_list.get(id=id).revision.revert()
    
            # restore related 
            deleted_list = reversion.get_deleted(Bar)
            bar_set = filter(lambda s: s.field_dict['foo'] == id, deleted_list)
    
            for bar in bar_set:
                bar.revision.revert()
    

    Then the Foo object need have a custom manager

    class Foo(models.Model):
        ...
        objects = FooQuerySet.as_manager()
    

    One can extract the related fields automatically from the model. But to do it manually is enough for my purposes.