from reversion import revisions
from django.db import transaction

# Monkeypatch the revisions
try:
    revisions.create_revision
except AttributeError:
    revisions.create_revision = revisions.revision_context_manager.create_revision

class AtomicVersionMixin(object):

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

    def delete(self, *args, **kwargs):
        with transaction.atomic(), revisions.create_revision():
            return super().delete(*args, **kwargs)

from reversion import revisions
from django.db import transaction

# Monkeypatch the revisions
try:
    revisions.create_revision
except AttributeError:
    revisions.create_revision = revisions.revision_context_manager.create_revision

class AtomicVersionMixin(object):
    """Makes sure saves and deletes go via transactions and version control
    even when objects are modified outside Django Admin"""
    def save(self, *args, **kwargs):
        with transaction.atomic(), revisions.create_revision():
            return super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        with transaction.atomic(), revisions.create_revision():
            return super().delete(*args, **kwargs)


class CleanSaveMixin(object):
    """Makes sure clean() is checked before object is saved"""
    def save(self, *args, **kwargs):
        if not kwargs.pop('skip_clean', False):
            self.full_clean()
        return super().save(*args, **kwargs)

Add a mixin for calling full_clean() on save()
Add a mixin for calling full_clean() on save()
