def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._default_manager.db_manager(using=using).create( revision=revision, **meta_fields)
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Only save versions that exist in the database. # Use _base_manager so we don't have problems when _default_manager is overriden model_db_pks = defaultdict(lambda: defaultdict(set)) for version in versions: model_db_pks[version._model][version.db].add(version.object_id) model_db_existing_pks = { model: { db: frozenset( map( force_str, model._base_manager.using(db).filter( pk__in=pks).values_list("pk", flat=True), )) for db, pks in db_pks.items() } for model, db_pks in model_db_pks.items() } versions = [ version for version in versions if version.object_id in model_db_existing_pks[version._model][version.db] ] # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=create_revision, revision=revision, versions=versions, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._base_manager.db_manager(using=using).create( revision=revision, **meta_fields) # Send the post_revision_commit signal. post_revision_commit.send( sender=create_revision, revision=revision, versions=versions, )
def update_metric(revision, sender, signal, versions, **kwargs): version2 = versions[0] fields2 = json.loads(version2.serialized_data)[0]['fields'] if version2.content_type_id == ContentType.objects.get_for_model(Probe).id: version1 = Version.objects.get_for_object( Probe.objects.get(id=version2.object_id))[1] # getting old probeversion from the second entry in VersionQuerySet fields1 = json.loads(version1.serialized_data)[0]['fields'] old_probeversion = u'%s (%s)' % (fields1['name'], fields1['version']) # getting new probeversion from the first entry in VersionQuerySet new_probeversion = u'%s (%s)' % (fields2['name'], fields2['version']) metric_pk = Metric.objects.filter( probeversion=old_probeversion).values_list('id', flat=True) ct = ContentType.objects.get_for_model(Metric) vers = list() for pk in metric_pk: instance = Metric.objects.get(id=pk) instance.probeversion = new_probeversion instance.probekey = version2 instance.save() LogEntry.objects.log_action(user_id=revision.user_id, content_type_id=ct.id, object_id=pk, object_repr=instance.__str__(), action_flag=CHANGE, change_message='Changed probeversion.') metric_ver = Version.objects.filter(object_id=pk, content_type_id=ct.id) rev = Revision(date_created=revision.date_created, user_id=revision.user_id, comment='[{"changed": {"fields": [' '"probeversion"]}}]') ver = Version(object_id=str(instance.id), content_type_id=ct.id, format=metric_ver[0].format, serialized_data=metric_ver[0].serialized_data, object_repr=metric_ver[0].object_repr, db='default') rev.save() Revision.objects.filter(pk=rev.id).update( date_created=revision.date_created) ver.revision = rev data = json.loads(metric_ver[0].serialized_data)[0] data['fields']['probeversion'] = new_probeversion data['fields']['probekey'] = version2.id ver.serialized_data = json.dumps([data]) vers.append(ver) Version.objects.bulk_create(vers)
def save_revision(self, objects, ignore_duplicates=False, user=None, comment="", meta=(), db=None): """Saves a new revision.""" # Get the db alias. db = db or DEFAULT_DB_ALIAS # Adapt the objects to a dict. if isinstance(objects, (list, tuple)): objects = dict( (obj, self.get_adapter(obj.__class__).get_version_data(obj, VERSION_CHANGE, db)) for obj in objects ) # Create the revision. if objects: # Follow relationships. for obj in self._follow_relationships(objects.iterkeys()): if not obj in objects: adapter = self.get_adapter(obj.__class__) objects[obj] = adapter.get_version_data(obj, VERSION_CHANGE) # Create all the versions without saving them ordered_objects = list(objects.iterkeys()) new_versions = [Version(**objects[obj]) for obj in ordered_objects] # Check if there's some change in all the revision's objects. save_revision = True if ignore_duplicates: # Find the latest revision amongst the latest previous version of each object. subqueries = [ Q(object_id=version.object_id, content_type=version.content_type) for version in new_versions ] subqueries = reduce(operator.or_, subqueries) latest_revision = self._get_versions(db).filter(subqueries).aggregate(Max("revision"))["revision__max"] # If we have a latest revision, compare it to the current revision. if latest_revision is not None: previous_versions = ( self._get_versions(db) .filter(revision=latest_revision) .values_list("serialized_data", flat=True) ) if len(previous_versions) == len(new_versions): all_serialized_data = [version.serialized_data for version in new_versions] if sorted(previous_versions) == sorted(all_serialized_data): save_revision = False # Only save if we're always saving, or have changes. if save_revision: # Save a new revision. revision = Revision(manager_slug=self._manager_slug, user=user, comment=comment) # Send the pre_revision_commit signal. pre_revision_commit.send(self, instances=ordered_objects, revision=revision, versions=new_versions) # Save the revision. revision.save(using=db) # Save version models. for version in new_versions: version.revision = revision version.save(using=db) # Save the meta information. for cls, kwargs in meta: cls._default_manager.db_manager(db).create(revision=revision, **kwargs) # Send the pre_revision_commit signal. post_revision_commit.send(self, instances=ordered_objects, revision=revision, versions=new_versions)
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Only save versions that exist in the database. # Use _base_manager so we don't have problems when _default_manager is overriden model_db_pks = defaultdict(lambda: defaultdict(set)) for version in versions: model_db_pks[version._model][version.db].add(version.object_id) model_db_existing_pks = { model: { db: frozenset(map( force_text, model._base_manager.using(db).filter(pk__in=pks).values_list("pk", flat=True), )) for db, pks in db_pks.items() } for model, db_pks in model_db_pks.items() } versions = [ version for version in versions if version.object_id in model_db_existing_pks[version._model][version.db] ] # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=create_revision, revision=revision, versions=versions, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._base_manager.db_manager(using=using).create( revision=revision, **meta_fields ) # Send the post_revision_commit signal. post_revision_commit.send( sender=create_revision, revision=revision, versions=versions, )
def save_revision(self, objects, user=None, comment='', meta=(), db=None): """Saves a new revision.""" # Create the revision. if objects: # Follow relationships. for obj, follower_type in self._follow_relationships( ((key, version_data.get('type')) for key, version_data in objects.items()) ): if not obj in objects: adapter = self.get_adapter(obj.__class__) follow_obj_data = adapter.get_version_data(obj) follow_obj_data['type'] = follower_type objects[obj] = follow_obj_data # Create all the versions without saving them ordered_objects = list(objects.keys()) new_versions = [Version(**objects[obj]) for obj in ordered_objects] # Check if there's some change in all the revision's objects. save_revision = True # Only save if we're always saving, or have changes. if save_revision: # Save a new revision. revision = Revision( manager_slug = self._manager_slug, user = user, comment = comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send(self, instances = ordered_objects, revision = revision, versions = new_versions, ) # Save the revision. with transaction.atomic(using=db): revision.save(using=db) # Save version models. for version in new_versions: version.revision = revision version.save() # Save the meta information. for cls, kwargs in meta: cls._default_manager.db_manager(db).create(revision=revision, **kwargs) # Send the post_revision_commit signal. post_revision_commit.send(self, instances = ordered_objects, revision = revision, versions = new_versions, ) # Return the revision. return revision
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._default_manager.db_manager(using=using).create( revision=revision, **meta_fields )
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Bail early if there are no objects to save. if not versions: return # Save a new revision. with transaction.atomic(using=using): # altered from original. Normally _save_revision is run wintin atomic context revision = Revision( date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=create_revision, revision=revision, versions=versions, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._default_manager.db_manager(using=using).create( revision=revision, **meta_fields ) # Send the post_revision_commit signal. post_revision_commit.send( sender=create_revision, revision=revision, versions=versions, )
def save_revision(self, objects, ignore_duplicates=False, user=None, comment="", meta=(), db=None): """Saves a new revision.""" # Get the db alias. db = db or DEFAULT_DB_ALIAS # Adapt the objects to a dict. if isinstance(objects, (list, tuple)): objects = dict( (obj, self.get_adapter(obj.__class__).get_version_data(obj, db)) for obj in objects ) # Create the revision. if objects: # Follow relationships. for obj in self._follow_relationships(objects.keys()): if not obj in objects: adapter = self.get_adapter(obj.__class__) objects[obj] = adapter.get_version_data(obj) # Create all the versions without saving them ordered_objects = list(objects.keys()) new_versions = [Version(**objects[obj]) for obj in ordered_objects] # Check if there's some change in all the revision's objects. save_revision = True if ignore_duplicates: # Find the latest revision amongst the latest previous version of each object. subqueries = [Q(object_id=version.object_id, content_type=version.content_type) for version in new_versions] subqueries = reduce(operator.or_, subqueries) latest_revision = self._get_versions(db).filter(subqueries).aggregate(Max("revision"))["revision__max"] # If we have a latest revision, compare it to the current revision. if latest_revision is not None: previous_versions = self._get_versions(db).filter(revision=latest_revision).values_list("serialized_data", flat=True) if len(previous_versions) == len(new_versions): all_serialized_data = [version.serialized_data for version in new_versions] if sorted(previous_versions) == sorted(all_serialized_data): save_revision = False # Only save if we're always saving, or have changes. if save_revision: # Save a new revision. revision = Revision( manager_slug = self._manager_slug, user = user, comment = comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send(self, instances = ordered_objects, revision = revision, versions = new_versions, ) # Save the revision. with transaction.atomic(using=db): revision.save(using=db) # Save version models. for version in new_versions: version.revision = revision Version.objects.using(db).bulk_create(new_versions) # Save the meta information. for cls, kwargs in meta: cls._default_manager.db_manager(db).create(revision=revision, **kwargs) # Send the pre_revision_commit signal. post_revision_commit.send(self, instances = ordered_objects, revision = revision, versions = new_versions, ) # Return the revision. return revision
def save_revision(self, objects, updated=None, inserted=None, deleted=None, ignore_duplicates=False, user=None, comment="", smart=True, meta=(), db=None): """Saves a new revision.""" request = current_task.request logger.info("Executing task id {id}, args: {args} retries: {retries}".format( id=request.id, args=request.args, retries=request.retries)) logger.info(request.kwargs) # Get the db alias. db = db or DEFAULT_DB_ALIAS # Adapt the objects to a dict. if isinstance(objects, (list, tuple)): objects = dict( (obj, self.get_adapter(obj.__class__).get_version_data(obj, VERSION_CHANGE, db)) for obj in objects ) # Create the revision. if objects: # Follow relationships. for obj in self._follow_relationships(objects.keys()): if not obj in objects: adapter = self.get_adapter(obj.__class__) objects[obj] = adapter.get_version_data(obj, VERSION_CHANGE) # Create all the versions without saving them ordered_objects = list(objects.keys()) new_versions = [Version(**objects[obj]) for obj in ordered_objects] # Check if there's some change in all the revision's objects. save_revision = True if ignore_duplicates: # Find the latest revision amongst the latest previous version of each object. subqueries = [Q(object_id=version.object_id, content_type=version.content_type) for version in new_versions] subqueries = reduce(operator.or_, subqueries) latest_revision = self._get_versions(db).filter(subqueries).aggregate(Max("revision"))["revision__max"] # If we have a latest revision, compare it to the current revision. if latest_revision is not None: previous_versions = (self._get_versions(db).filter(revision=latest_revision) .values_list("serialized_data", flat=True)) if len(previous_versions) == len(new_versions): all_serialized_data = [version.serialized_data for version in new_versions] if sorted(previous_versions) == sorted(all_serialized_data): save_revision = False # Only save if we're always saving, or have changes. if save_revision: # Save a new revision. if not comment and smart: deleted_comments, inserted_comments, updated_comments = [], [], [] for deleted_instance in deleted: deleted_comments.append(was_deleted_message.format(get_object_smart_repr(deleted_instance))) for inserted_instance in inserted: #here we must get the instances what was created inserted_comments.append(was_created_message.format(get_object_smart_repr(inserted_instance))) for serialized_updated_instance in updated: old_instance_deserialized = deserialize(VersionAdapter.format, serialized_updated_instance).next() new_instance = None change_list = [] for instance in objects: if (old_instance_deserialized.object.__class__ == instance.__class__ and old_instance_deserialized.object.pk == instance.pk): new_instance = instance break fields = sorted(new_instance._meta.fields + new_instance._meta.many_to_many, key=lambda x: x.creation_counter) exclude_fields = self.get_adapter(new_instance.__class__).exclude fields = [field for field in fields if field.name not in exclude_fields] for field in fields: old_value, new_value = (field.value_from_object(old_instance_deserialized.object), field.value_from_object(new_instance)) # Set false old and new values to None because of next != operator(types..) if not old_value: old_value = None if not new_value: new_value = None if old_value != new_value: if not isinstance(field, (AutoField, ManyToManyField)): if isinstance(field, RelatedField): if old_value: value_from = get_object_smart_repr(field.rel.to.objects.get(pk=old_value)) else: value_from = no_value_message if new_value: value_to = get_object_smart_repr(field.rel.to.objects.get(pk=new_value)) else: value_to = no_value_message change_list.append(changes_template.format(verbose_name=field.verbose_name, value_from=value_from, value_to=value_to)) else: value_from = old_value if old_value else no_value_message value_to = new_value if new_value else no_value_message change_list.append(changes_template.format(verbose_name=field.verbose_name, value_from=value_from, value_to=value_to)) elif isinstance(field, ManyToManyField): old_value = old_instance_deserialized.m2m_data[field.name] old_value = [int(pk) for pk in old_value] if old_value else old_value new_value = [obj.pk for obj in new_value] if new_value else new_value if old_value: value_from = u", ".join(get_object_smart_repr(field.rel.to.objects.get(pk=m2m_pk)) for m2m_pk in old_value) else: value_from = no_value_message if new_value: value_to = u", ".join(get_object_smart_repr(field.rel.to.objects.get(pk=m2m_pk)) for m2m_pk in new_value) else: value_to = no_value_message if old_value != new_value: change_list.append(changes_template.format(verbose_name=field.verbose_name, value_from=value_from, value_to=value_to)) if change_list: object_repr = get_object_smart_repr(old_instance_deserialized.object) change_list.insert(0, was_changed_message.format(object_repr)) if change_list: inserted_comments_string = u"\n".join(change_list) else: new_instance_repr = get_object_smart_repr(new_instance) inserted_comments_string = saved_without_changes_message.format(new_instance_repr) inserted_comments.append(inserted_comments_string) comment = u"\n".join(inserted_comments + updated_comments + deleted_comments) revision = Revision( manager_slug=self._manager_slug, user=user, user_string=unicode(user) if user else u"", comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send(self, instances=ordered_objects, revision=revision, versions=new_versions) # Save the revision. revision.save(using=db) # Save version models. for version in new_versions: version.revision = revision version.save(using=db) # Save the meta information. for cls, kwargs in meta: cls._default_manager.db_manager(db).create(revision=revision, **kwargs) # Send the pre_revision_commit signal. post_revision_commit.send(self, instances=ordered_objects, revision=revision, versions=new_versions,) # Return the revision. return revision
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Only save versions that exist in the database. # Use _base_manager so we don't have problems when _default_manager is overriden model_db_pks = defaultdict(lambda: defaultdict(set)) for version in versions: model_db_pks[version._model][version.db].add(version.object_id) model_db_existing_pks = { model: { db: frozenset( map( force_str, model._base_manager.using(db).filter( pk__in=pks).values_list("pk", flat=True), )) for db, pks in db_pks.items() } for model, db_pks in model_db_pks.items() } versions = [ version for version in versions if version.object_id in model_db_existing_pks[version._model][version.db] ] # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=create_revision, revision=revision, versions=versions, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_inst_or_model, meta_fields in meta: # Default meta class and primary key meta_class = meta_inst_or_model pk = None # Add the revision to the fields that will be added/updated meta_fields['revision'] = revision # If the supplied meta_model is an instance, we need to ensure the # correct class is used when updating as well as be able to query # the meta instance properly if isinstance(meta_inst_or_model, (models.Model, )): # Fetch the class used to make the update meta_class = meta_inst_or_model.__class__ pk = meta_inst_or_model.pk # Call update or create meta_class._base_manager.db_manager(using=using).update_or_create( pk=pk, defaults=meta_fields) # Send the post_revision_commit signal. post_revision_commit.send( sender=create_revision, revision=revision, versions=versions, )
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Only save versions that exist in the database. model_db_pks = defaultdict(lambda: defaultdict(set)) for version in versions: model_db_pks[version._model][version.db].add(version.object_id) model_db_existing_pks = { model: { db: frozenset(map( force_text, model._default_manager.using(db).filter(pk__in=pks).values_list("pk", flat=True), )) for db, pks in db_pks.items() } for model, db_pks in model_db_pks.items() } custom_models = getattr(settings, 'DJANGO_REVISION_CUSTOM_MODELS', False) if custom_models: if hasattr(settings, 'DJANGO_REVISION_EXCLUDED_MODELS'): versions = [ version for version in versions if version._model._meta.object_name not in settings.DJANGO_REVISION_EXCLUDED_MODELS and version.object_id in model_db_existing_pks[version._model][version.db] ] elif hasattr(settings, 'DJANGO_REVISION_ALLOWED_MODELS'): versions = [ version for version in versions if version._model._meta.object_name in settings.DJANGO_REVISION_ALLOWED_MODELS and version.object_id in model_db_existing_pks[version._model][version.db] ] else: raise ImproperlyConfigured( "Using 'DJANGO_REVISION_CUSTOM_MODELS' without either the 'DJANGO_REVISION_EXCLUDED_MODELS' in" " settings or the 'DJANGO_REVISION_ALLOWED_MODELS' in settings is prohibited.") else: versions = [ version for version in versions if version.object_id in model_db_existing_pks[version._model][version.db] ] # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=create_revision, revision=revision, versions=versions, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._default_manager.db_manager(using=using).create( revision=revision, **meta_fields ) # Send the post_revision_commit signal. post_revision_commit.send( sender=create_revision, revision=revision, versions=versions, )
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Only save versions that exist in the database. model_db_pks = defaultdict(lambda: defaultdict(set)) for version in versions: model_db_pks[version._model][version.db].add(version.object_id) model_db_existing_pks = { model: { db: frozenset( map( force_text, model._default_manager.using(db).filter( pk__in=pks).values_list("pk", flat=True), )) for db, pks in db_pks.items() } for model, db_pks in model_db_pks.items() } versions = [ version for version in versions if version.object_id in model_db_existing_pks[version._model][version.db] ] # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=create_revision, revision=revision, versions=versions, ) # Save the revision. revision.save(using=using) # Save version models. # XXX magic numbers because base content needs to be saved before children BASE_CONTENT_TYPE_ID = 18 versions = sorted( versions, key=lambda v: (0 if v.content_type_id == BASE_CONTENT_TYPE_ID else v.content_type_id)) for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._default_manager.db_manager(using=using).create( revision=revision, **meta_fields) # Send the post_revision_commit signal. post_revision_commit.send( sender=create_revision, revision=revision, versions=versions, )
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): from reversion.models import Revision # Only save versions that exist in the database. # Use _base_manager so we don't have problems when _default_manager is overriden model_db_pks = defaultdict(lambda: defaultdict(set)) for version in versions: model_db_pks[version._model][version.db].add(version.object_id) def _safe_get_pks(model, db, pks): """ Hack: helper function for use with django-safedelete package so we avoid committing versions for objects that have already been safe deleted See hack in models VersionQuerySet get_deleted too """ try: from safedelete.models import is_safedelete_cls except ImportError: is_safedelete_cls = lambda: False if is_safedelete_cls(model): # Use the safedelete manager to ensure we exclude "soft-deleted" # obj versions return model._default_manager.using(db).filter(pk__in=pks)\ .values_list("pk", flat=True) # Else: not using softdelete for this model so use base manager as in # original django-revision return model._base_manager.using(db).filter(pk__in=pks)\ .values_list("pk", flat=True) model_db_existing_pks = { model: { db: frozenset(map( force_text, _safe_get_pks(model, db, pks), )) for db, pks in db_pks.items() } for model, db_pks in model_db_pks.items() } versions = [ version for version in versions if version.object_id in model_db_existing_pks[version._model][version.db] ] # Bail early if there are no objects to save. if not versions: return # Save a new revision. revision = Revision( date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=create_revision, revision=revision, versions=versions, ) # Save the revision. revision.save(using=using) # Save version models. for version in versions: version.revision = revision version.save(using=using) # Save the meta information. for meta_model, meta_fields in meta: meta_model._base_manager.db_manager(using=using).create( revision=revision, **meta_fields) # Send the post_revision_commit signal. post_revision_commit.send( sender=create_revision, revision=revision, versions=versions, )
def save_revision(self, objects=(), ignore_duplicates=False, user=None, comment="", meta=(), date_created=None, db=None): """ Manually saves a new revision containing the given objects. `objects` is an iterable of model instances. `serialized_objects` is an iterable of dicts of version data. """ from django.contrib.contenttypes.models import ContentType from reversion.models import Revision, Version date_created = timezone.now() if date_created is None else date_created # Create the object versions. version_data_dict = {} for obj in objects: # Handle eagerly-saved version dicts. if isinstance(obj, dict): version_data_seq = (obj,) # Handle model instances. else: version_data_seq = ( self.get_adapter(relation.__class__).get_version_data(relation) for relation in self._follow_relationships(obj) ) # Store the version data. version_data_dict.update( ((version_data["content_type"], version_data["object_id"]), version_data) for version_data in version_data_seq ) new_versions = [ Version( content_type=ContentType.objects.db_manager(db).get_by_natural_key(*version_data["content_type"]), object_id=version_data["object_id"], db=version_data["db"], format=version_data["format"], serialized_data=version_data["serialized_data"], object_repr=version_data["object_repr"], ) for version_data in version_data_dict.values() ] # Bail early if there are no objects to save. if not new_versions: return # Check for duplicates, if requested. save_revision = True if ignore_duplicates: # Find the latest revision amongst the latest previous version of each object. latest_revision_qs = Revision.objects.using(db).annotate( version_count=models.Count("version"), ).filter( version_count=len(new_versions), manager_slug=self._manager_slug, ) for version in new_versions: latest_revision_qs = latest_revision_qs.filter( version__object_id=version.object_id, version__content_type_id=version.content_type_id, version__db=version.db, ) latest_revision = latest_revision_qs.order_by("-pk").first() # If we have a latest revision, compare it to the current revision. if latest_revision is not None: previous_versions = latest_revision.version_set.all() # Creates a sorted list of version keys for comparison. def get_version_keys(versions): return [ (version.object_id, version.content_type_id, version.db, version.local_field_dict) for version in sorted(versions, key=lambda v: (v.object_id, v.content_type_id, v.db)) ] save_revision = get_version_keys(previous_versions) != get_version_keys(new_versions) # Only save if we're always saving, or have changes. if save_revision: # Save a new revision. revision = Revision( manager_slug=self._manager_slug, date_created=date_created, user=user, comment=comment, ) # Send the pre_revision_commit signal. pre_revision_commit.send( sender=self, instances=objects, revision=revision, versions=new_versions, ) # Save the revision. with transaction.atomic(using=db): revision.save(using=db) # Save version models. for version in new_versions: version.revision = revision version.save(using=db) # Save the meta information. for meta_obj in meta: meta_obj.revision = revision meta_obj.save(using=db) # Send the post_revision_commit signal. post_revision_commit.send( sender=self, instances=objects, revision=revision, versions=new_versions, ) # Return the revision. return revision