def paired_object_safe_to_delete(base_object): collector = NestedObjects(using='default') collector.collect([base_object]) collected = collector.nested() if len(collected) > 2: return False assert collected[0] == base_object if len(collected) == 1: return True if len(collected[1]) != 1: return False assert collected[1][0] == base_object.extra return True
def cascade_restore(self, collection=None): if not collection: collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect(self) collection = collector.nested() try: collection.undelete() except AttributeError: try: for x in collection: self.cascade_restore(x) except TypeError: pass
def exclude_item(obj, stdout): collector = NestedObjects(using="default") collector.collect([obj]) stdout.write("Os seguintes objetos serão excluídos:") for item in collector.nested(): str_item(item, stdout) stdout.write("Os seguintes objetos impedem a exclusão:") for item in collector.protected: str_item(item, stdout) continuar = input("Deseja continuar: [s/n] ") if continuar == "s": stdout.write(str(obj.delete())) stdout.write("Excluído com sucesso")
def get_related_objects(obj, using=DEFAULT_DB_ALIAS): # This code is based on https://github.com/makinacorpus/django-safedelete collector = NestedObjects(using=using) collector.collect([obj]) def flatten(elem): if isinstance(elem, list): return itertools.chain.from_iterable(map(flatten, elem)) elif obj != elem: return (elem, ) return () return flatten(collector.nested())
def get_translatable_children(self, obj): """ Obtain all the translatable children from "obj" :param obj: :return: """ collector = NestedObjects(using='default') collector.collect([obj]) object_list = collector.nested() items = self.get_elements(object_list) # avoid first object because it's the main object return items[1:]
def get_info_before_delete_user(self, remove_sounds=False, remove_user=False): """ This method can be called before delete_user to display to the user the elements that will be modified """ ret = {} if remove_sounds: sounds = Sound.objects.filter(user=self.user) packs = Pack.objects.filter(user=self.user) collector = NestedObjects(using='default') collector.collect(sounds) ret['deleted'] = collector ret['logic_deleted'] = packs if remove_user: collector = NestedObjects(using='default') collector.collect([self.user]) ret['deleted'] = collector ret['anonymised'] = self return ret
def get_deleted_objects(self): collector = NestedObjects(using=router.db_for_write(self.model)) collector.collect([self.object]) perms_needed = set() def format_callback(obj): p = '%s.%s' % ( obj._meta.app_label, get_permission_codename('delete', obj._meta) ) if not self.request.user.has_perm(p): perms_needed.add(obj._meta.verbose_name) registered = obj.__class__ in self.request.djangobmf_site.modules # only show bmf modules if not registered: return None return format_html( '{0}: {1}', obj._meta.verbose_name, obj ) def format_protected_callback(obj): # if obj.__class__ in self.request.djangobmf_site.modules and not obj._bmfmeta.only_related: # return format_html( # '{0}: <a href="{1}">{2}</a>', # obj._meta.verbose_name, # obj.bmfmodule_detail(), # obj # ) # else: return format_html( '{0}: {1}', obj._meta.verbose_name, obj ) to_delete = collector.nested(format_callback) protected = [ format_protected_callback(obj) for obj in collector.protected ] return to_delete, perms_needed, protected
def safe_delete(self, model): collector = NestedObjects(using=connection.cursor().db.alias) collector.collect([model]) if len(collector.nested()) > 1: related_objects = "\n\t".join( [repr(m) for m in collector.nested()[1:]] ) raise UnsafeToDelete( "Can't delete '{}' with related objects: \n {}".format( model, related_objects ) ) return model.delete()
def get_context_data(self, **kwargs): context = super(BaseDeleteView, self).get_context_data(**kwargs) collector = NestedObjects(using='default') collector.collect([self.get_object()]) context['deleted_objects'] = collector.nested() if self.model: context['model_name'] = self.model._meta.verbose_name.title() context[ 'model_name_plural'] = self.model._meta.verbose_name_plural.title( ) return context
def related_objects(obj): """ Return a generator to the objects that would be deleted if we delete "obj" (excluding obj) """ collector = NestedObjects(using=router.db_for_write(obj)) collector.collect([obj]) def flatten(elem): if isinstance(elem, list): return itertools.chain.from_iterable(map(flatten, elem)) elif obj != elem: return (elem, ) return () return flatten(collector.nested())
def get_deleted_objects(self, objs, user, using='default'): """ Find all objects related to ``objs`` that should also be deleted. ``objs`` must be a homogeneous iterable of objects (e.g. a QuerySet). Return a nested list of strings suitable for display in the template with the ``unordered_list`` filter. Encontre todos os objetos relacionados a ``objs`` que também devem ser deletados. ``objs`` deve ser um iterável homogêneo de objetos (por exemplo, um QuerySet). Retornar uma lista aninhada de sequências adequadas para exibição no template com o filtro `` unordered_list``. """ collector = NestedObjects(using=using) collector.collect(objs) perms_needed = set() def format_callback(obj): opts = obj._meta no_edit_link = '%s: %s' % (str(opts.verbose_name).title(), obj) try: url = reverse('%s:%s-update'% ( opts.app_label, opts.model_name), None, (quote(obj.pk),)) except NoReverseMatch: # Change url doesn't exist -- don't display link to edit return no_edit_link p = '%s.%s' % (opts.app_label, get_permission_codename('delete', opts)) if not user.has_perm(p): perms_needed.add(opts.verbose_name.title()) # Display a link to the admin page. return format_html('{}: <a href="{}">{}</a>', str(opts.verbose_name).title(), url, obj) to_delete = collector.nested(format_callback) protected = [format_callback(obj) for obj in collector.protected] model_count = {model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items()} return perms_needed, protected
def get_deleted_objects(objs): collector = NestedObjects(using='default') collector.collect([objs]) def format_callback(obj): opts = obj._meta no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj)) return no_edit_link # to_delete = collector.nested(format_callback) to_delete = collector.nested() protected = [format_callback(obj) for obj in collector.protected] model_count = {model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items()} return to_delete, model_count, protected
def get_deleted_objects(objs, request, admin_site): """ Patched django/contrib/admin/utils.py to skip collecting links for related nested objects """ try: obj = objs[0] except IndexError: return [], {}, set(), [] else: using = router.db_for_write(obj._meta.model) collector = NestedObjects(using=using) collector.collect(objs) model_count = {model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items()} to_delete = ['{}: {}'.format(cap_words(k), v) for k, v in model_count.items()] return to_delete, model_count, None, None
def get_protected_related(entity): """ Return a list of objects that prevent this entity from being deleted """ using = router.db_for_write(entity) collector = NestedObjects(using=using) collector.collect([entity]) def callable_func(obj): data = { 'pk': obj.pk, 'verbose_name': obj._meta.verbose_name, 'verbose_name_plural': str(obj._meta.verbose_name_plural) } return data return [callable_func(o) for o in collector.protected]
def delete_engagement(request, eid): engagement = get_object_or_404(Engagement, pk=eid) product = engagement.product form = DeleteEngagementForm(instance=engagement) if request.method == 'POST': if 'id' in request.POST and str(engagement.id) == request.POST['id']: form = DeleteEngagementForm(request.POST, instance=engagement) if form.is_valid(): del engagement.tags engagement.delete() messages.add_message(request, messages.SUCCESS, 'Engagement and relationships removed.', extra_tags='alert-success') create_notification( event='other', title='Deletion of %s' % engagement.name, description='The engagement "%s" was deleted by %s' % (engagement.name, request.user), url=request.build_absolute_uri( reverse('view_engagements', args=(product.id, ))), recipients=[engagement.lead], icon="exclamation-triangle") if engagement.engagement_type == 'CI/CD': return HttpResponseRedirect( reverse("view_engagements_cicd", args=(product.id, ))) else: return HttpResponseRedirect( reverse("view_engagements", args=(product.id, ))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([engagement]) rels = collector.nested() product_tab = Product_Tab(product.id, title="Delete Engagement", tab="engagements") product_tab.setEngagement(engagement) return render( request, 'dojo/delete_engagement.html', { 'product_tab': product_tab, 'engagement': engagement, 'form': form, 'rels': rels, })
def delete_data(): for user in User.objects.filter(id__in=[ 602, 654, 616, 611, 577, 613, 424, 580, 598, 574, 590, 579, 581, 588, 575, 645, 626, 576, 597, 615, 637, 50, 13, 181, 592, 552, 559, 189, 163, 641, 620, 558, 591, 638, 634, 636, 627, 642, 628, 612, 609, 560, 630, 646, 639, 578, 551, 564, 606, 38, 46, 583, 589, 557, 656, 655, 633, 566, 610, 595, 562, 619, 596, 607, 649, 650, 644, 586, 198, 553, 197, 568, 572, 561, 632, 622, 608, 621, 605, 569, 618, 617, 570, 640, 402, 584, 593, 624, 603, 623, 582, 550, 635, 563, 601, 585, 600, 652, 27, 565, 614, 599, 648, 464, 555, 554, 567, 643, 604, 629, 631, 556, 653, 571, 587, 573, 15, 14 ]): # for user in User.objects.filter(username='******'): collector = NestedObjects(using='default') # or specific database collector.collect([user]) to_delete = collector.nested() print(to_delete, user.profile.name, user.profile.phone)
def process_preview(self, request, form, context): if "_delete" in request.POST: context['action'] = "_delete" def format_callback(obj): return '%s: %s' % (capfirst(obj._meta.verbose_name), obj) ps = form.cleaned_data['obj_list'] collector = NestedObjects(using='default') # or specific database collector.collect(ps) to_delete = collector.nested(format_callback) context['objs'] = to_delete else: self.preview_actions(request, form, context)
def xls_export(self, _models, root=None, root_qs=None): if (root and root_qs) or ((root or root_qs) is None): raise RuntimeError( _("Either a root object or a root queryset must be provided")) workbook = None try: workbookfile = self.dest or NamedTemporaryFile(dir=self.tmpdir, delete=False) workbook = Workbook() del workbook['Sheet'] sheets = {} lmodels = {} for k, v in _models.items(): lname = k.lower() model_name = lname.rsplit('.')[1] lmodels[lname] = v sheets[model_name] = workbook.create_sheet(title=model_name) sheets[model_name].append(v) if root: root_qs = root._meta.model.objects.filter(pk=root.pk) using = router.db_for_write(root_qs.first()._meta.model) collector = NestedObjects(using=using) collector.collect(root_qs) def callback(obj): fields = lmodels.get(obj._meta.label_lower, None) if fields: sheets[obj._meta.model_name].append( [getattr(obj, x) for x in fields]) collector.nested(callback) workbook.save(workbookfile) return workbookfile.name except Exception as e: if workbook: if not workbookfile.closed: workbookfile.close() if os.path.exists(workbookfile.name): os.remove(workbookfile.name) raise e
def delete_queryset(self, request, queryset): # this method is called by delete_selected and can be overridden try: obj = queryset[0] except IndexError: return else: using = router.db_for_write(obj._meta.model) collector = NestedObjects(using=using) collector.collect(queryset) to_delete = [] for item, value in collector.model_objs.items(): to_delete += value to_delete += collector.protected # since we are only performing soft delete, we must soft_delete related objects too, if possible for obj in to_delete: if hasattr(obj, 'soft_delete'): obj.soft_delete()
def remove_dupes(apps, schema_editor): Person = apps.get_model("people", "Person") Leaflet = apps.get_model("leaflets", "Leaflet") qs = (Person.objects.values("remote_id").annotate( count=Count("remote_id")).filter(count__gt=1)) for person_dict in qs: print(person_dict) models = Person.objects.filter(remote_id=person_dict["remote_id"]) keep_person = models[0] dupes = models[1:] for person in dupes: person.leaflet_set.update(publisher_person=keep_person) collector = NestedObjects(using="default") collector.collect([person]) collected = collector.nested() assert len(collected) < 2 or not any( [isinstance(x, Leaflet) for x in collected[1]]) person.delete()
def serialize_public_draws(file_): backup_cutoff = dt.datetime.now(dt.timezone.utc) - dt.timedelta(days=10) backup_ids = set() collector = NestedObjects(using="default") for result in models.Result.objects.filter( schedule_date__gt=backup_cutoff): draw = result.draw if draw.id in backup_ids: continue collector.collect([draw]) backup_ids.add(draw.id) for ss in models.SecretSantaResult.objects.filter( created_at__gt=backup_cutoff): collector.collect([ss]) for payment in models.Payment.objects.filter(created_at__gt=backup_cutoff): collector.collect([payment]) all_draw_objects = itertools.chain.from_iterable(collector.data.values()) serializers.serialize("json", all_draw_objects, stream=file_)
def get_delete_cascade(request, title): document = get_object_or_404(Document, url_title=title) check_permissions(document, request.user, [document.edit_permission_name]) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([document]) delete_cascade = collector.nested() # remove all subclasses of current document from the list because that does not add much helpful information simplified_delete_cascade = [] for cascade_item in delete_cascade: if issubclass(type(document), type( cascade_item)) and not type(document) == type(cascade_item): continue simplified_delete_cascade.append(cascade_item) return HttpResponse( json.dumps(delete_cascade_to_json(simplified_delete_cascade)))
def delete_test(request, tid): test = get_object_or_404(Test, pk=tid) eng = test.engagement form = DeleteTestForm(instance=test) if request.method == 'POST': if 'id' in request.POST and str(test.id) == request.POST['id']: form = DeleteTestForm(request.POST, instance=test) if form.is_valid(): product = test.engagement.product test.delete() messages.add_message(request, messages.SUCCESS, 'Test and relationships removed.', extra_tags='alert-success') create_notification( event='other', title='Deletion of %s' % test.title, product=product, description='The test "%s" was deleted by %s' % (test.title, request.user), url=request.build_absolute_uri( reverse('view_engagement', args=(eng.id, ))), recipients=[test.engagement.lead], icon="exclamation-triangle") return HttpResponseRedirect( reverse('view_engagement', args=(eng.id, ))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([test]) rels = collector.nested() product_tab = Product_Tab(test.engagement.product.id, title="Delete Test", tab="engagements") product_tab.setEngagement(test.engagement) return render( request, 'dojo/delete_test.html', { 'test': test, 'product_tab': product_tab, 'form': form, 'rels': rels, 'deletable_objects': rels, })
def get_deleted_objects(objs, user): """ Find all objects related to ``objs`` that should also be deleted. ``objs`` must be a homogeneous iterable of objects (e.g. a QuerySet). Returns a nested list of strings suitable for display in the template with the ``unordered_list`` filter. Copied and updated from django.contrib.admin.utils for front end display. """ using = router.db_for_write(objs[0].__class__) collector = NestedObjects(using=using) collector.collect(objs) perms_needed = set() def format_callback(obj): opts = obj._meta no_edit_link = '%s: %s' % (capfirst( opts.verbose_name), force_text(obj)) p = '%s.%s' % (opts.app_label, get_permission_codename('delete', opts)) if not user.has_perm(p): perms_needed.add(opts.verbose_name) if hasattr(obj, 'get_absolute_url'): url = obj.get_absolute_url() # Display a link to the admin page. return format_html('{}: <a href="{}">{}</a>', capfirst(opts.verbose_name), url, obj) else: # Don't display link to edit, because it either has no # admin or is edited inline. return no_edit_link to_delete = collector.nested(format_callback) protected = [format_callback(obj) for obj in collector.protected] model_count = { model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items() } return to_delete, model_count, perms_needed, protected
def get_deleted_objects(objs, request, admin_site): """ Find all objects related to ``objs`` that should also be deleted. ``objs`` must be a homogeneous iterable of objects (e.g. a QuerySet). Return a nested list of strings suitable for display in the template with the ``unordered_list`` filter. """ try: obj = objs[0] except IndexError: return [], {}, set(), [] else: using = router.db_for_write(obj._meta.model) collector = NestedObjects(using=using) collector.collect(objs) perms_needed = set() def format_callback(obj): model = obj.__class__ has_admin = model in admin_site._registry opts = obj._meta no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), obj) if has_admin: # Display a link to the admin page. return format_html('{}: <a href="{}">{}</a>', capfirst(opts.verbose_name), obj.get_absolute_url, obj) else: # Don't display link to edit, because it either has no # admin or is edited inline. return no_edit_link to_delete = collector.nested(format_callback) protected = [format_callback(obj) for obj in collector.protected] model_count = { model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items() } return to_delete, model_count, perms_needed, protected
def delete_jira(request, tid): jira_instance = get_object_or_404(JIRA_Instance, pk=tid) # eng = test.engagement # TODO Make Form form = DeleteJIRAInstanceForm(instance=jira_instance) if request.method == 'POST': if 'id' in request.POST and str( jira_instance.id) == request.POST['id']: form = DeleteJIRAInstanceForm(request.POST, instance=jira_instance) if form.is_valid(): try: jira_instance.delete() messages.add_message( request, messages.SUCCESS, 'JIRA Conf and relationships removed.', extra_tags='alert-success') create_notification( event='other', title='Deletion of JIRA: %s' % jira_instance.configuration_name, description='JIRA "%s" was deleted by %s' % (jira_instance.configuration_name, request.user), url=request.build_absolute_uri(reverse('jira')), ) return HttpResponseRedirect(reverse('jira')) except Exception as e: add_error_message_to_response( 'Unable to delete JIRA Instance, probably because it is used by JIRA Issues: %s' % str(e)) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([jira_instance]) rels = collector.nested() add_breadcrumb(title="Delete", top_level=False, request=request) return render( request, 'dojo/delete_jira.html', { 'inst': jira_instance, 'form': form, 'rels': rels, 'deletable_objects': rels, })
def soft_delete_selected(modeladmin, request, queryset): """ In charge of deactivating instances instead of deleting them """ opts = modeladmin.model._meta using = router.db_for_write(modeladmin.model) count = queryset.count() if not count: return None # soft delete selected items if hasattr(queryset, 'soft_delete'): queryset.soft_delete() else: for obj in queryset: soft_delete_item = getattr(obj, 'soft_delete', None) if not soft_delete_item: modeladmin.message_user( request, _("{verbose_name_plural} has not soft delete available.". format({"verbose_name_plural": opts.verbose_name_plural})), messages.SUCCESS) return None soft_delete_item() # soft delete nested items collector = NestedObjects(using=using) collector.collect(queryset) for model_base, nested_objects_collected in collector.data.items(): if model_base == modeladmin.model or not hasattr( model_base, 'soft_delete'): continue for nested_object in nested_objects_collected: nested_object.soft_delete() # response success message modeladmin.message_user( request, _("Successfully soft deleted {count} {items}.".format( count=count, items=model_ngettext(modeladmin.opts, count))), messages.SUCCESS) return None
def delete_finding_group(request, fgid): logger.debug('delete finding group: %s', fgid) finding_group = get_object_or_404(Finding_Group, pk=fgid) form = DeleteFindingGroupForm(instance=finding_group) if request.method == 'POST': if 'id' in request.POST and str( finding_group.id) == request.POST['id']: form = DeleteFindingGroupForm(request.POST, instance=finding_group) if form.is_valid(): product = finding_group.test.engagement.product finding_group.delete() messages.add_message( request, messages.SUCCESS, 'Finding Group and relationships removed.', extra_tags='alert-success') create_notification( event='other', title='Deletion of %s' % finding_group.name, product=product, description='The finding group "%s" was deleted by %s' % (finding_group.name, request.user), url=request.build_absolute_uri( reverse('view_test', args=(finding_group.test.id, ))), icon="exclamation-triangle") return HttpResponseRedirect( reverse('view_test', args=(finding_group.test.id, ))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([finding_group]) rels = collector.nested() product_tab = Product_Tab(finding_group.test.engagement.product.id, title="Product", tab="settings") return render( request, 'dojo/delete_finding_group.html', { 'finding_group': finding_group, 'form': form, 'product_tab': product_tab, 'rels': rels, })
def _get_protected(self): using = router.db_for_write(self.object) collector = NestedObjects(using=using) collector.collect([self.object]) def callable_func(obj): meta = obj._meta data = { 'object': obj, 'verbose_name': obj._meta.verbose_name, 'has_permission': self.request.user.has_perm('%s.delete_%s' % (meta.app_label, meta.model_name)) } return data return [callable_func(o) for o in collector.protected]
def soft_delete_cascade_policy_action(self, **kwargs): # Soft-delete on related objects before for related in related_objects(self): if is_safedelete_cls(related.__class__) and not getattr(related, FIELD_NAME): related.delete(force_policy=SOFT_DELETE, **kwargs) # soft-delete the object self._delete(force_policy=SOFT_DELETE, **kwargs) collector = NestedObjects(using=router.db_for_write(self)) collector.collect([self]) # update fields (SET, SET_DEFAULT or SET_NULL) for model, instances_for_fieldvalues in collector.field_updates.items(): for (field, value), instances in instances_for_fieldvalues.items(): query = models.sql.UpdateQuery(model) query.update_batch( [obj.pk for obj in instances], {field.name: value}, collector.using, )