def revert(self, delete=False): """Reverts all objects in this revision.""" version_set = self.version_set.all() # Optionally delete objects no longer in the current revision. if delete: # Get a dict of all objects in this revision. old_revision = {} for version in version_set: try: obj = version.object except ContentType.objects.get_for_id(version.content_type_id).model_class().DoesNotExist: pass else: old_revision[obj] = version # Calculate the set of all objects that are in the revision now. from reversion.revisions import RevisionManager manager = RevisionManager.get_manager(self.manager_slug) current_revision = manager._follow_relationships(obj for obj in old_revision.keys() if obj is not None) # Delete objects that are no longer in the current revision. for item in current_revision: if item._get_pk_val(): try: if item in old_revision: if old_revision[item].type == VERSION_DELETE: item.delete() else: item.delete() except (ObjectDoesNotExist, AssertionError): # assert with Pk none. continue # Attempt to revert all revisions. versions = [version for version in version_set if version.type != VERSION_DELETE] safe_revert(versions) reverted.send(sender=self, revision=self, versions=versions)
def revert(self, delete=False): """Reverts all objects in this revision.""" version_set = self.version_set.all() # Optionally delete objects no longer in the current revision. if delete: # Get a dict of all objects in this revision. old_revision = {} for version in version_set: try: obj = version.object except ContentType.objects.get_for_id(version.content_type_id).model_class().DoesNotExist: pass else: old_revision[obj] = version # Calculate the set of all objects that are in the revision now. from reversion.revisions import RevisionManager current_revision = RevisionManager.get_manager(self.manager_slug)._follow_relationships(old_revision.keys()) # Delete objects that are no longer in the current revision. for item in current_revision: if item in old_revision: if old_revision[item].type == VERSION_DELETE: item.delete() else: item.delete() # Attempt to revert all revisions. safe_revert([version for version in version_set if version.type != VERSION_DELETE])
def revert(self, delete=False): """Reverts all objects in this revision.""" version_set = self.version_set.all() # Optionally delete objects no longer in the current revision. if delete: # Get a dict of all objects in this revision. old_revision = {} for version in version_set: try: obj = version.object except ContentType.objects.get_for_id( version.content_type_id).model_class().DoesNotExist: pass else: old_revision[obj] = version # Calculate the set of all objects that are in the revision now. from reversion.revisions import RevisionManager current_revision = RevisionManager.get_manager( self.manager_slug)._follow_relationships( obj for obj in old_revision.keys() if obj is not None) # Delete objects that are no longer in the current revision. for item in current_revision: if item not in old_revision: item.delete() # Attempt to revert all revisions. safe_revert(version_set)
def revert(self, delete=False): """Reverts all objects in this revision.""" version_set = self.version_set.all() # Optionally delete objects no longer in the current revision. if delete: # Get a dict of all objects in this revision. old_revision = set() for version in version_set: try: obj = version.object except ContentType.objects.get_for_id(version.content_type_id).model_class().DoesNotExist: pass else: old_revision.add(obj) # Calculate the set of all objects that are in the revision now. from reversion.revisions import RevisionManager current_revision = RevisionManager.get_manager(self.manager_slug)._follow_relationships( obj for obj in old_revision if obj is not None ) # Delete objects that are no longer in the current revision. for item in current_revision: if item not in old_revision: item.delete() # Attempt to revert all revisions. safe_revert(version_set)
def revert(self, delete=False): """Reverts all objects in this revision.""" version_set = self.version_set.all() # Optionally delete objects no longer in the current revision. if delete: # Get a dict of all objects in this revision. old_revision = {} for version in version_set: try: obj = version.object except ContentType.objects.get_for_id( version.content_type_id).model_class().DoesNotExist: pass else: old_revision[obj] = version # Calculate the set of all objects that are in the revision now. from reversion.revisions import RevisionManager current_revision = RevisionManager.get_manager( self.manager_slug)._follow_relationships(old_revision.keys()) # Delete objects that are no longer in the current revision. for item in current_revision: if item in old_revision: if old_revision[item].type == VERSION_DELETE: item.delete() else: item.delete() # Attempt to revert all revisions. def do_revert(versions): unreverted_versions = [] for version in versions: try: version.revert() except IntegrityError: unreverted_versions.append(version) if len(unreverted_versions) == len(versions): raise RevertError( "Could not revert revision, due to database integrity errors." ) if unreverted_versions: do_revert(unreverted_versions) do_revert([ version for version in version_set if version.type != VERSION_DELETE ])
def handle(self, *app_labels, **options): # Activate project's default language translation.activate(settings.LANGUAGE_CODE) # Load admin classes. admin.autodiscover() # Parse options. comment = options["comment"] batch_size = options["batch_size"] revision_manager = RevisionManager.get_manager(options["manager"]) database = options["database"] verbosity = int(options.get("verbosity", 1)) model_classes = parse_app_labels(revision_manager, app_labels) # Create revisions. with transaction.atomic(using=database): for model_class in model_classes: self.create_initial_revisions(model_class, comment, batch_size, verbosity, revision_manager, database) # Go back to default language translation.deactivate()
def object_version(self): """The stored version of the model.""" data = self.serialized_data data = force_text(data.encode("utf8")) obj = self.object reverted_obj = list( serializers.deserialize(self.format, data, ignorenonexistent=True))[0] from reversion.revisions import RevisionManager manager = RevisionManager.get_manager(self.revision.manager_slug) fields = manager.get_adapter(self.content_type.model_class()).fields for field in fields: setattr(obj, field, getattr(reverted_obj.object, field)) reverted_obj.object = obj return reverted_obj
def revert(self, delete=False): """Reverts all objects in this revision.""" version_set = self.version_set.all() # Optionally delete objects no longer in the current revision. if delete: # Get a dict of all objects in this revision. old_revision = {} for version in version_set: try: obj = version.object except ContentType.objects.get_for_id(version.content_type_id).model_class().DoesNotExist: pass else: old_revision[obj] = version # Calculate the set of all objects that are in the revision now. from reversion.revisions import RevisionManager current_revision = RevisionManager.get_manager(self.manager_slug)._follow_relationships(old_revision.keys()) # Delete objects that are no longer in the current revision. for item in current_revision: if item in old_revision: if old_revision[item].type == VERSION_DELETE: item.delete() else: item.delete() # Attempt to revert all revisions. def do_revert(versions): unreverted_versions = [] for version in versions: try: version.revert() except IntegrityError: unreverted_versions.append(version) if len(unreverted_versions) == len(versions): raise RevertError("Could not revert revision, due to database integrity errors.") if unreverted_versions: do_revert(unreverted_versions) do_revert([version for version in version_set if version.type != VERSION_DELETE])
self.assertEqual(ReversionTestModel1.objects.count(), 1) # Recover the test model. with reversion.create_revision(): reversion.get_deleted(ReversionTestModel1)[0].revision.revert() # Make sure it was recovered. self.assertEqual(TestFollowModel.objects.count(), 1) self.assertEqual(ReversionTestModel1.objects.count(), 2) def tearDown(self): reversion.unregister(TestFollowModel) TestFollowModel.objects.all().delete() del self.follow1 super(FollowModelsTest, self).tearDown() excluded_revision_manager = RevisionManager("excluded") class ExcludedFieldsTest(RevisionTestBase): def setUp(self): excluded_revision_manager.register(ReversionTestModel1, fields=("id", )) excluded_revision_manager.register(ReversionTestModel2, exclude=("name", )) super(ExcludedFieldsTest, self).setUp() def testExcludedRevisionManagerIsSeparate(self): self.assertEqual( excluded_revision_manager.get_for_object(self.test11).count(), 1) def testExcludedFieldsAreRespected(self):
def revision_manager(self): return RevisionManager.get_manager(self.manager_slug)
class PetAdmin(CompareVersionAdmin): pass admin.site.register(Pet, PetAdmin) class VariantModelAdmin(CompareVersionAdmin): pass admin.site.register(VariantModel, VariantModelAdmin) custom_revision_manager = RevisionManager("custom") class CustomModelAdmin(CompareVersionAdmin): revision_manager = custom_revision_manager admin.site.register(CustomModel, CustomModelAdmin) admin.site.register(Identity, CustomModelAdmin) """ class RelatedModelInline(admin.StackedInline): model = RelatedModel class GenericRelatedInline(GenericStackedInline):
def handle(self, *app_labels, **options): days = options["days"] keep = options["keep"] force = options["force"] interactive = options["interactive"] # Load admin classes. admin.autodiscover() # Check for deprecated confirmation option. if not options["confirmation"]: interactive = False warnings.warn( ( "--no-confirmation is deprecated, please use --no-input instead. " "--no-confirmation will be removed in django-reversion 1.12.0" ), DeprecationWarning ) revision_manager = RevisionManager.get_manager(options["manager"]) database = options.get("database") verbosity = int(options.get("verbosity", 1)) # Parse date. date = None if options["date"]: if days: raise CommandError("You cannot use --date and --days at the same time. They are exclusive.") try: date = datetime.datetime.strptime(options["date"], "%Y-%m-%d").date() except ValueError: raise CommandError(( "The date you gave (%s) is not a valid date. ", "The date should be in the ISO format (YYYY-MM-DD)." ) % options["date"]) # Find the date from the days arguments. elif days: date = datetime.date.today() - datetime.timedelta(days) # Build the queries revision_query = Revision.objects.using(database).filter( manager_slug=revision_manager._manager_slug, ) if date: revision_query = revision_query.filter(date_created__lt=date) if app_labels: model_classes = parse_app_labels(revision_manager, app_labels) content_types = [ revision_manager._get_content_type(model_class, db=database) for model_class in model_classes ] revision_query = revision_query.filter(version__content_type__in=content_types) if not force: excluded_content_types = ContentType.objects.db_manager(database).exclude(pk__in=[ content_type.pk for content_type in content_types ]) revision_query = revision_query.exclude(version__content_type__in=excluded_content_types) # Handle keeping n versions. if keep: objs = Version.objects.using(database).filter( revision__manager_slug=revision_manager._manager_slug, revision__in=revision_query, ) # Get all the objects that have more than the maximum revisions. objs = objs.values("object_id", "content_type_id", "db").annotate( total_ver=Count("object_id"), ).filter(total_ver__gt=keep) # Get all ids of the oldest revisions minus the max allowed revisions for all objects. revisions_not_kept = set() for obj in objs: revisions_not_kept.update(list(Version.objects.using(database).filter( content_type__id=obj["content_type_id"], object_id=obj["object_id"], db=obj["db"], ).order_by("-pk").values_list("revision_id", flat=True)[keep:])) revision_query = revision_query.filter(pk__in=revisions_not_kept) revision_count = revision_query.count() # Ask confirmation if interactive: choice = input("Are you sure you want to delete %s revisions? [y|N] " % revision_count) if choice.lower() != "y": self.stdout.write("Aborting revision deletion.") return # Delete versions and revisions if verbosity >= 2: self.stdout.write("Deleting %s revisions..." % revision_count) # Delete the revisions. with transaction.atomic(using=database): revision_query.delete() # Final logging. if verbosity >= 2: self.stdout.write("Deleted %s revisions." % revision_count)