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
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, )
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
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 _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, 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
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
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