def get_deleted(self, model_class): """ Returns all the deleted versions for the given model class. The results are returned with the most recent versions first. """ content_type = ContentType.objects.get_for_model(model_class) live_pk_queryset = model_class._default_manager.all().values_list("pk", flat=True) versioned_objs = self._get_versions().filter(content_type=content_type) if has_int_pk(model_class): # We can do this as a fast, in-database join. deleted_version_pks = versioned_objs.exclude(object_id_int__in=live_pk_queryset).values_list( "object_id_int" ) else: # This join has to be done as two separate queries. deleted_version_pks = versioned_objs.exclude(object_id__in=list(live_pk_queryset.iterator())).values_list( "object_id" ) deleted_version_pks = ( deleted_version_pks.exclude(type=VERSION_DELETE) .annotate(latest_pk=Max("pk")) .values_list("latest_pk", flat=True) ) return self._get_versions().filter(pk__in=deleted_version_pks).order_by("-pk")
def get_deleted(self, model_class, db=None, model_db=None): """ Returns all the deleted versions for the given model class. The results are returned with the most recent versions first. """ db = db or DEFAULT_DB_ALIAS model_db = model_db or db content_type = ContentType.objects.db_manager(db).get_for_model(model_class) live_pk_queryset = model_class._default_manager.db_manager(model_db).all().values_list("pk", flat=True) versioned_objs = self._get_versions(db).filter( content_type = content_type, ) if has_int_pk(model_class): # If the model and version data are in different databases, decouple the queries. if model_db != db: live_pk_queryset = list(live_pk_queryset.iterator()) # We can do this as a fast, in-database join. deleted_version_pks = versioned_objs.exclude( object_id_int__in = live_pk_queryset ).values_list("object_id_int") else: # This join has to be done as two separate queries. deleted_version_pks = versioned_objs.exclude( object_id__in = list(live_pk_queryset.iterator()) ).values_list("object_id") deleted_version_pks = deleted_version_pks.annotate( latest_pk = Max("pk") ).values_list("latest_pk", flat=True) # HACK: MySQL deals extremely badly with this as a subquery, and can hang infinitely. # TODO: If a version is identified where this bug no longer applies, we can add a version specifier. if connection.vendor == "mysql": deleted_version_pks = list(deleted_version_pks) # Return the deleted versions! return self._get_versions(db).filter(pk__in=deleted_version_pks).order_by("-pk")
def __init__(self, field, field_name, obj, version1, version2, manager, is_reversed): self.field = field self.field_name = field_name self.obj = obj model = self.obj.__class__ self.has_int_pk = has_int_pk(model) self.adapter = manager.get_adapter(model) # VersionAdapter instance # is a related field (ForeignKey, ManyToManyField etc.) self.is_related = getattr(self.field, 'rel', None) is not None self.is_reversed = is_reversed if not self.is_related: self.follow = None elif self.field_name in self.adapter.follow: self.follow = True else: self.follow = False self.compare_obj1 = CompareObject(field, field_name, obj, version1, self.has_int_pk, self.adapter) self.compare_obj2 = CompareObject(field, field_name, obj, version2, self.has_int_pk, self.adapter) self.value1 = self.compare_obj1.value self.value2 = self.compare_obj2.value
def get_queryset(self, request): """Returns the annotated queryset.""" content_type = ContentType.objects.get_for_model(self.model) pk = self.model._meta.pk if has_int_pk(self.model): version_table_field = "object_id_int" else: version_table_field = "object_id" return super(VersionMetaAdmin, self).get_queryset(request).extra( select={ "date_modified": """ SELECT MAX(%(revision_table)s.date_created) FROM %(version_table)s JOIN %(revision_table)s ON %(revision_table)s.id = %(version_table)s.revision_id WHERE %(version_table)s.content_type_id = %%s AND %(version_table)s.%(version_table_field)s = %(table)s.%(pk)s """ % { "revision_table": connection.ops.quote_name(Revision._meta.db_table), "version_table": connection.ops.quote_name(Version._meta.db_table), "table": connection.ops.quote_name(self.model._meta.db_table), "pk": connection.ops.quote_name(pk.db_column or pk.attname), "version_table_field": connection.ops.quote_name(version_table_field), } }, select_params=(content_type.id, ), )
def get_deleted(self, model_class): """ Returns all the deleted versions for the given model class. The results are returned with the most recent versions first. """ content_type = ContentType.objects.get_for_model(model_class) live_pk_queryset = model_class._default_manager.all().values_list( "pk", flat=True) versioned_objs = self._get_versions().filter( content_type=content_type, ) if has_int_pk(model_class): # We can do this as a fast, in-database join. deleted_version_pks = versioned_objs.exclude( object_id_int__in=live_pk_queryset).values_list( "object_id_int") else: # This join has to be done as two separate queries. deleted_version_pks = versioned_objs.exclude(object_id__in=list( live_pk_queryset.iterator())).values_list("object_id") deleted_version_pks = deleted_version_pks.exclude( type=VERSION_DELETE, ).annotate(latest_pk=Max("pk")).values_list( "latest_pk", flat=True) return self._get_versions().filter( pk__in=deleted_version_pks).order_by("-pk")
def get_queryset(self, request): """Returns the annotated queryset.""" content_type = ContentType.objects.get_for_model(self.model) pk = self.model._meta.pk if has_int_pk(self.model): version_table_field = "object_id_int" else: version_table_field = "object_id" return super(VersionMetaAdmin, self).get_queryset(request).extra( select = { "date_modified": """ SELECT MAX(%(revision_table)s.date_created) FROM %(version_table)s JOIN %(revision_table)s ON %(revision_table)s.id = %(version_table)s.revision_id WHERE %(version_table)s.content_type_id = %%s AND %(version_table)s.%(version_table_field)s = %(table)s.%(pk)s """ % { "revision_table": connection.ops.quote_name(Revision._meta.db_table), "version_table": connection.ops.quote_name(Version._meta.db_table), "table": connection.ops.quote_name(self.model._meta.db_table), "pk": connection.ops.quote_name(pk.db_column or pk.attname), "version_table_field": connection.ops.quote_name(version_table_field), } }, select_params = (content_type.id,), )
def get_deleted(self, model_class, db=None, model_db=None): """ Returns all the deleted versions for the given model class. The results are returned with the most recent versions first. """ db = db or DEFAULT_DB_ALIAS model_db = model_db or db content_type = ContentType.objects.db_manager(db).get_for_model(model_class) live_pk_queryset = model_class._default_manager.db_manager(model_db).all().values_list("pk", flat=True) versioned_objs = self._get_versions(db).filter( content_type = content_type, ) if has_int_pk(model_class): # If the model and version data are in different databases, decouple the queries. if model_db != db: live_pk_queryset = list(live_pk_queryset.iterator()) # We can do this as a fast, in-database join. deleted_version_pks = versioned_objs.exclude( object_id_int__in = live_pk_queryset ).values_list("object_id_int") else: # This join has to be done as two separate queries. deleted_version_pks = versioned_objs.exclude( object_id__in = list(live_pk_queryset.iterator()) ).values_list("object_id") deleted_version_pks = deleted_version_pks.exclude( type = VERSION_DELETE, ).annotate( latest_pk = Max("pk") ).values_list("latest_pk", flat=True) return self._get_versions(db).filter(pk__in=deleted_version_pks).order_by("-pk")
def create_initial_revisions(self, app, model_class, comment, batch_size, verbosity=2, **kwargs): """Creates the set of initial revisions for the given model.""" # Import the relevant admin module. try: import_module("%s.admin" % app.__name__.rsplit(".", 1)[0]) except ImportError: pass # Check all models for empty revisions. if default_revision_manager.is_registered(model_class): created_count = 0 content_type = ContentType.objects.get_for_model(model_class) versioned_pk_queryset = Version.objects.filter( content_type=content_type).all() live_objs = model_class._default_manager.all() if has_int_pk(model_class): # We can do this as a fast database join! live_objs = live_objs.exclude( pk__in=versioned_pk_queryset.values_list("object_id_int", flat=True)) else: # This join has to be done as two separate queries. live_objs = live_objs.exclude(pk__in=list( versioned_pk_queryset.values_list("object_id", flat=True).iterator())) # Save all the versions. ids = list( live_objs.values_list(model_class._meta.pk.name, flat=True)) total = len(ids) for i in xrange(0, total, batch_size): chunked_ids = ids[i:i + batch_size] objects = live_objs.in_bulk(chunked_ids) for id, obj in objects.iteritems(): try: default_revision_manager.save_revision((obj, ), comment=comment) except: print "ERROR: Could not save initial version for %s %s." % ( model_class.__name__, obj.pk) raise created_count += 1 reset_queries() if verbosity >= 2: print u"Created %s of %s." % (created_count, total) # Print out a message, if feeling verbose. if verbosity >= 2: print u"Created %s initial revision(s) for model %s." % ( created_count, smart_unicode( model_class._meta.verbose_name)) else: if verbosity >= 2: print u"Model %s is not registered." % (smart_unicode( model_class._meta.verbose_name))
def forwards(self, orm): "Write your forwards methods here." for version in orm.Version.objects.all().iterator(): try: content_type = ContentType.objects.get_for_id(version.content_type_id) except AttributeError: version.delete() # This version refers to a content type that doesn't exist any more. continue model = content_type.model_class() if has_int_pk(model): version.object_id_int = int(version.object_id) version.save()
def delete_for_object_reference(self, model, object_id, db=None): db = db or DEFAULT_DB_ALIAS content_type = ContentType.objects.db_manager(db).get_for_model(model) versions = self._get_versions(db).filter( content_type = content_type, ).select_related("revision") if has_int_pk(model): # We can do this as a fast, indexed lookup. object_id_int = int(object_id) versions.filter(object_id_int=object_id_int).delete() else: # We can't do this using an index. Never mind. object_id = force_text(object_id) versions.filter(object_id=object_id).delete() return 0
def create_initial_revisions(self, app, model_class, comment, batch_size, verbosity=2, database=None, **kwargs): """Creates the set of initial revisions for the given model.""" # Import the relevant admin module. try: import_module("%s.admin" % app.__name__.rsplit(".", 1)[0]) except ImportError: pass # Check all models for empty revisions. if default_revision_manager.is_registered(model_class): if verbosity >= 2: print("Creating initial revision(s) for model %s ..." % (force_text(model_class._meta.verbose_name))) created_count = 0 content_type = ContentType.objects.db_manager(database).get_for_model(model_class) versioned_pk_queryset = Version.objects.using(database).filter(content_type=content_type).all() live_objs = model_class._default_manager.using(database).all() if has_int_pk(model_class): # We can do this as a fast database join! live_objs = live_objs.exclude( pk__in = versioned_pk_queryset.values_list("object_id_int", flat=True) ) else: # This join has to be done as two separate queries. live_objs = live_objs.exclude( pk__in = list(versioned_pk_queryset.values_list("object_id", flat=True).iterator()) ) # Save all the versions. ids = list(live_objs.values_list(model_class._meta.pk.name, flat=True)) total = len(ids) for i in range(0, total, batch_size): chunked_ids = ids[i:i+batch_size] objects = live_objs.in_bulk(chunked_ids) for id, obj in objects.items(): try: default_revision_manager.save_revision((obj,), comment=comment, db=database) except: print("ERROR: Could not save initial version for %s %s." % (model_class.__name__, obj.pk)) raise created_count += 1 reset_queries() if verbosity >= 2: print("Created %s of %s." % (created_count, total)) # Print out a message, if feeling verbose. if verbosity >= 2: print("Created %s initial revision(s) for model %s." % (created_count, force_text(model_class._meta.verbose_name))) else: if verbosity >= 2: print("Model %s is not registered." % (force_text(model_class._meta.verbose_name)))
def get_version_data(self, obj, db=None): """Creates the version data to be saved to the version model.""" object_id = force_text(obj.pk) content_type = ContentType.objects.db_manager(db).get_for_model(obj) if has_int_pk(obj.__class__): object_id_int = int(obj.pk) else: object_id_int = None return { "object_id": object_id, "object_id_int": object_id_int, "content_type": content_type, "format": self.get_serialization_format(), "serialized_data": self.get_serialized_data(obj), "object_repr": force_text(obj), }
def get_version_data(self, obj, type_flag): """Creates the version data to be saved to the version model.""" object_id = unicode(obj.pk) content_type = ContentType.objects.get_for_model(obj) if has_int_pk(obj.__class__): object_id_int = int(obj.pk) else: object_id_int = None return { "object_id": object_id, "object_id_int": object_id_int, "content_type": content_type, "format": self.get_serialization_format(), "serialized_data": self.get_serialized_data(obj), "object_repr": unicode(obj), "type": type_flag, }
def get_version_data(self, obj, type_flag): """Creates the version data to be saved to the version model.""" object_id = unicode(obj.pk) content_type = ContentType.objects.get_for_model(obj) if has_int_pk(obj.__class__): object_id_int = int(obj.pk) else: object_id_int = None return { "object_id": object_id, "object_id_int": object_id_int, "content_type": content_type, "format": self.get_serialization_format(), "serialized_data": self.get_serialized_data(obj), "object_repr": unicode(obj), "type": type_flag }
def get_version_data(self, obj, type_flag, db=None): """Creates the version data to be saved to the version model.""" object_id = unicode(obj.pk) db = db or django.db.DEFAULT_DB_ALIAS content_type = ContentType.objects.db_manager(db).get_for_model(obj) if has_int_pk(obj.__class__): object_id_int = int(obj.pk) else: object_id_int = None return { "object_id": object_id, "object_id_int": object_id_int, "content_type": content_type, "format": self.get_serialization_format(), "serialized_data": self.get_serialized_data(obj), "object_repr": unicode(obj), "type": type_flag }
def get_for_object_reference(self, model, object_id): """ Returns all versions for the given object reference. The results are returned with the most recent versions first. """ content_type = ContentType.objects.get_for_model(model) versions = self._get_versions().filter(content_type=content_type).select_related("revision") if has_int_pk(model): # We can do this as a fast, indexed lookup. object_id_int = int(object_id) versions = versions.filter(object_id_int=object_id_int) else: # We can't do this using an index. Never mind. object_id = unicode(object_id) versions = versions.filter(object_id=object_id) versions = versions.order_by("-pk") return versions
def get_for_object_reference(self, model, object_id, db=None): """ Returns all versions for the given object reference. The results are returned with the most recent versions first. """ content_type = ContentType.objects.db_manager(db).get_for_model(model) versions = self._get_versions(db).filter( content_type=content_type, ).select_related("revision") if has_int_pk(model): # We can do this as a fast, indexed lookup. object_id_int = int(object_id) versions = versions.filter(object_id_int=object_id_int) else: # We can't do this using an index. Never mind. object_id = force_text(object_id) versions = versions.filter(object_id=object_id) versions = versions.order_by("-pk") return versions
def get_for_object_reference(self, model, object_id, db=None): """ Returns all versions for the given object reference. The results are returned with the most recent versions first. """ content_type = ContentType.objects.db_manager(db).get_for_model(model) versions = self._get_versions(db).filter( content_type = content_type, ).select_related('revision') if has_int_pk(model): # We can do this as a fast, indexed lookup. object_id_int = int(object_id) versions = versions.filter(object_id_int=object_id_int) else: # We can't do this using an index. Never mind. object_id = force_text(object_id) versions = versions.filter(object_id=object_id) versions = versions.order_by('-pk') return versions
def get_for_object_reference(self, model, object_id, db=None): """ Returns all versions for the given object reference. The results are returned with the most recent versions first. """ if isinstance(model, (str, unicode)): model = get_model(*model.split(".")) db = db or DEFAULT_DB_ALIAS content_type = ContentType.objects.db_manager(db).get_for_model(model) versions = self._get_versions(db).filter(content_type=content_type,).select_related("revision") if has_int_pk(model): # We can do this as a fast, indexed lookup. object_id_int = int(object_id) versions = versions.filter(object_id_int=object_id_int) else: # We can't do this using an index. Never mind. object_id = force_text(object_id) versions = versions.filter(object_id=object_id) versions = versions.order_by("-pk") return versions
def get_version_data(self, obj, type_flag, db=None): """Creates the version data to be saved to the version model.""" object_id = force_text(obj.pk) db = db or DEFAULT_DB_ALIAS content_type = ContentType.objects.db_manager(db).get_for_model(obj) if has_int_pk(obj.__class__): object_id_int = int(obj.pk) else: object_id_int = None object_repr = None try: object_repr = unicode(obj) except: object_repr = u"Error occurred while unicoding object" return { "object_id": object_id, "object_id_int": object_id_int, "content_type": content_type, "format": self.get_serialization_format(), "serialized_data": self.get_serialized_data(obj), "object_repr": object_repr, "type": type_flag }
def create_initial_revisions(self, app, model_class, comment, verbosity=2, **kwargs): """Creates the set of initial revisions for the given model.""" # Import the relevant admin module. try: import_module("%s.admin" % app.__name__.rsplit(".", 1)[0]) except ImportError: pass # Check all models for empty revisions. if default_revision_manager.is_registered(model_class): created_count = 0 content_type = ContentType.objects.get_for_model(model_class) versioned_pk_queryset = Version.objects.filter(content_type=content_type).all() live_objs = model_class._default_manager.all() if has_int_pk(model_class): # We can do this as a fast database join! live_objs = live_objs.exclude( pk__in = versioned_pk_queryset.values_list("object_id_int", flat=True) ) else: # This join has to be done as two separate queries. live_objs = live_objs.exclude( pk__in = list(versioned_pk_queryset.values_list("object_id", flat=True).iterator()) ) # Save all the versions. for obj in live_objs: try: default_revision_manager.save_revision((obj,), comment=comment) except: print "ERROR: Could not save initial version for %s %s." % (model_class.__name__, obj.pk) raise created_count += 1 # Print out a message, if feeling verbose. if verbosity >= 2: print u"Created %s initial revision(s) for model %s." % (created_count, smart_unicode(model_class._meta.verbose_name)) else: if verbosity >= 2: print u"Model %s is not registered." % (smart_unicode(model_class._meta.verbose_name))
def __init__(self, field, field_name, obj, version1, version2, manager): self.field = field self.field_name = field_name self.obj = obj model = self.obj.__class__ self.has_int_pk = has_int_pk(model) self.adapter = manager.get_adapter(model) # VersionAdapter instance # is a related field (ForeignKey, ManyToManyField etc.) self.is_related = self.field.rel is not None if not self.is_related: self.follow = None elif self.field_name in self.adapter.follow: self.follow = True else: self.follow = False self.compare_obj1 = CompareObject(field, field_name, obj, version1, self.has_int_pk, self.adapter) self.compare_obj2 = CompareObject(field, field_name, obj, version2, self.has_int_pk, self.adapter) self.value1 = self.compare_obj1.value self.value2 = self.compare_obj2.value
def __init__(self, *args, **kwargs): """Initializes the VersionMetaAdmin.""" super(VersionMetaAdmin, self).__init__(*args, **kwargs) # Check that the model has an int pk. if not has_int_pk(self.model): raise ImproperlyConfigured("Cannot use VersionMetaAdmin unless the model has an integer primary key.")