Esempio n. 1
0
def save_revision(date_created, user, comment, versions, using):
    from reversion.revisions import create_revision

    # Generate random revision PK
    revision_id = str(uuid4())

    user_key = get_key_from_object(user)

    revision = ReversionDynamoModel(revision_id=revision_id,
                                    object_key=NULL_OBJ_KEY,
                                    date_created=date_created,
                                    user_key=user_key,
                                    comment=comment)

    # Send the pre_revision_commit signal.
    pre_revision_commit.send(sender=create_revision,
                             revision=revision,
                             versions=versions)

    # Save version models.
    with Version.batch_write() as batch:
        batch.save(revision)

        for version in versions:
            version.revision_id = revision_id
            version.date_created = date_created
            version.user_key = user_key
            version.comment = comment
            batch.save(version)
    post_revision_commit.send(sender=create_revision,
                              revision=revision,
                              versions=versions,
                              revision_id=revision_id)
    return revision_id
Esempio n. 2
0
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,
    )
    # 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,
    )
Esempio n. 3
0
def save_revision(date_created, user, comment, versions, using):
    from reversion.revisions import create_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)
    post_revision_commit.send(sender=create_revision,
                              revision=revision,
                              versions=versions,
                              revision_id=revision.id)
    return revision.pk
Esempio n. 4
0
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,
    )
Esempio n. 5
0
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,
    )
Esempio n. 6
0
 def save_revision(self,
                   objects,
                   ignore_duplicates=False,
                   user=None,
                   comment="",
                   meta=(),
                   db=None):
     """Saves a new revision."""
     # 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.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
Esempio n. 7
0
 def save_revision(self, objects, ignore_duplicates=False, user=None, comment="", meta=(), db=None):
     """Saves a new revision."""
     from reversion.models import Revision, Version, has_int_pk
     # 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 obj not 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.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
Esempio n. 8
0
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,
    )
Esempio n. 9
0
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,
    )
Esempio n. 10
0
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,
    )
Esempio n. 11
0
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,
    )
Esempio n. 12
0
    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