class NestedObjectsTests(TestCase): """ Tests for ``NestedObject`` utility collection. """ def setUp(self): self.n = NestedObjects(using=DEFAULT_DB_ALIAS) self.objs = [Count.objects.create(num=i) for i in range(5)] def _check(self, target): self.assertEqual(self.n.nested(lambda obj: obj.num), target) def _connect(self, i, j): self.objs[i].parent = self.objs[j] self.objs[i].save() def _collect(self, *indices): self.n.collect([self.objs[i] for i in indices]) def test_unrelated_roots(self): self._connect(2, 1) self._collect(0) self._collect(1) self._check([0, 1, [2]]) def test_siblings(self): self._connect(1, 0) self._connect(2, 0) self._collect(0) self._check([0, [1, 2]]) def test_non_added_parent(self): self._connect(0, 1) self._collect(0) self._check([0]) def test_cyclic(self): self._connect(0, 2) self._connect(1, 0) self._connect(2, 1) self._collect(0) self._check([0, [1, [2]]]) def test_queries(self): self._connect(1, 0) self._connect(2, 0) # 1 query to fetch all children of 0 (1 and 2) # 1 query to fetch all children of 1 and 2 (none) # Should not require additional queries to populate the nested graph. self.assertNumQueries(2, self._collect, 0) def test_on_delete_do_nothing(self): """ Check that the nested collector doesn't query for DO_NOTHING objects. """ n = NestedObjects(using=DEFAULT_DB_ALIAS) objs = [Event.objects.create()] EventGuide.objects.create(event=objs[0]) with self.assertNumQueries(2): # One for Location, one for Guest, and no query for EventGuide n.collect(objs)
def delete_rule(request, tid): rule = get_object_or_404(Rule, pk=tid) form = DeleteRuleForm(instance=rule) if request.method == 'POST': print >> sys.stderr, 'id' in request.POST print >> sys.stderr, str(rule.id) == request.POST['id'] print >> sys.stderr, str(rule.id) == request.POST['id'] # if 'id' in request.POST and str(rule.id) == request.POST['id']: form = DeleteRuleForm(request.POST, instance=rule) print >> sys.stderr, form.is_valid() print >> sys.stderr, form.errors print >> sys.stderr, form.non_field_errors() print >> sys.stderr, 'id' in request.POST if form.is_valid(): rule.delete() messages.add_message(request, messages.SUCCESS, 'Rule deleted.', extra_tags='alert-success') return HttpResponseRedirect(reverse('rules')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([rule]) rels = collector.nested() add_breadcrumb(parent=rule, title="Delete", top_level=False, request=request) system_settings = System_Settings.objects.get() return render(request, 'dojo/delete_rule.html', {'rule': rule, 'form': form, 'active_tab': 'findings', 'system_settings': system_settings, 'rels': rels, })
def find_all_related_objects(model): collector = NestedObjects(using="default") # database name collector.collect([model]) # list of objects. Single one won't do objects = [] for obj_set in collector.data.values(): objects += obj_set return objects
def hand_clean_DELETE(self): """ We don't validate the 'DELETE' field itself because on templates it's not rendered using the field information, but just using a generic "deletion_field" of the InlineModelAdmin. """ if self.cleaned_data.get(DELETION_FIELD_NAME, False): using = router.db_for_write(self._meta.model) collector = NestedObjects(using=using) if self.instance.pk is None: return collector.collect([self.instance]) if collector.protected: objs = [] for p in collector.protected: objs.append( # Translators: Model verbose name and instance representation, # suitable to be an item in a list. _('%(class_name)s %(instance)s') % { 'class_name': p._meta.verbose_name, 'instance': p }) params = { 'class_name': self._meta.model._meta.verbose_name, 'instance': self.instance, 'related_objects': get_text_list(objs, _('and')) } msg = _( "Deleting %(class_name)s %(instance)s would require " "deleting the following protected related objects: " "%(related_objects)s") raise ValidationError(msg, code='deleting_protected', params=params)
def _cascade_soft_delete(inst_or_qs, using, keep_parents=False): """ Return collector instance that has marked ArchiveMixin instances for archive (i.e. update) instead of actual delete. Arguments: inst_or_qs (models.Model or models.QuerySet): the instance(s) that are to be deleted. using (db connection/router): the db to delete from. keep_parents (bool): defaults to False. Determine if cascade is true. Returns: models.deletion.Collector: this is a standard Collector instance but the ArchiveMixin instances are in the fields for update list. """ if not isinstance(inst_or_qs, models.QuerySet): instances = [inst_or_qs] else: instances = inst_or_qs # The collector will iteratively crawl the relationships and # create a list of models and instances that are connected to # this instance. collector = NestedObjects(using=using) collector.collect(instances, keep_parents=keep_parents) if collector.protected: raise models.ProtectedError("Delete protected", collector.protected) collector.sort() return collector
def delete(self, cascade=True, **kwargs): if self.PREVENT_DELETE: raise DeleteNotPermitted() if cascade: collector = NestedObjects(using='default') collector.collect([self]) field_updates = collector.field_updates for cls, to_update in field_updates.iteritems(): for (field, value), instances in to_update.iteritems(): cls.objects.filter( pk__in={o.pk for o in instances} ).update( **{field.attname: value} ) for klass, objs in collector.data.iteritems(): try: klass._meta.get_field('is_void') except models.FieldDoesNotExist: pass else: klass.objects.filter(pk__in={o.pk for o in objs}).update( is_void=True ) else: self.is_void = True self.save() signals.post_delete.send( sender=self.__class__, instance=self )
def delete_sql_data(self): for model_class, queryset in get_querysets_to_dump(self.domain_name, []): collector = NestedObjects(using=queryset.db) collector.collect(queryset) collector.delete() self.assertEqual([], list(get_objects_to_dump(self.domain_name, [])))
def delete_jira(request, tid): jira_instance = get_object_or_404(JIRA_Conf, pk=tid) # eng = test.engagement # TODO Make Form form = DeleteJIRAConfForm(instance=jira_instance) if request.method == 'POST': if 'id' in request.POST and str(jira_instance.id) == request.POST['id']: form = DeleteJIRAConfForm(request.POST, instance=jira_instance) if form.is_valid(): jira_instance.delete() messages.add_message(request, messages.SUCCESS, 'JIRA Conf and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('jira')) 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 delete_user(request, uid): user = get_object_or_404(Dojo_User, id=uid) form = DeleteUserForm(instance=user) from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([user]) rels = collector.nested() if user.id == request.user.id: messages.add_message(request, messages.ERROR, 'You may not delete yourself.', extra_tags='alert-danger') return HttpResponseRedirect(reverse('edit_user', args=(user.id, ))) if request.method == 'POST': if 'id' in request.POST and str(user.id) == request.POST['id']: form = DeleteUserForm(request.POST, instance=user) if form.is_valid(): user.delete() messages.add_message(request, messages.SUCCESS, 'User and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('users')) add_breadcrumb(title="Delete User", top_level=False, request=request) return render(request, 'dojo/delete_user.html', { 'to_delete': user, 'form': form, 'rels': rels, })
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(): del test.tags test.delete() messages.add_message(request, messages.SUCCESS, 'Test and relationships removed.', extra_tags='alert-success') 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(self, request, *args, **kwargs): #pr = get_object_or_404(Problem, tournament=request.user.profile.tournament, id=id) objs = self.get_objects(request, *args, **kwargs) def format_callback(obj): #print("callbacked %s"%(capfirst(obj._meta.verbose_name),) ) # if type(obj) == JurorGrade: return '%s' % (capfirst(obj._meta.verbose_name),) else: return '%s: %s' % (capfirst(obj._meta.verbose_name), obj) collector = NestedObjects(using='default') # or specific database #collector.collect(objs) if type(objs) == list: to_delete = [] for obj in objs: collector.collect([obj]) to_delete.append(collector.nested(format_callback)) else: collector.collect(objs) to_delete = collector.nested(format_callback) return render(request, "dashboard/delObjPreview.html", context={'objs': to_delete})
def hand_clean_DELETE(self): """ We don't validate the 'DELETE' field itself because on templates it's not rendered using the field information. """ if self.cleaned_data.get(DELETION_FIELD_NAME, False): using = router.db_for_write(self._meta.model) collector = NestedObjects(using=using) if self.instance._state.adding: return collector.collect([self.instance]) if collector.protected: objs = [] for p in collector.protected: objs.append( # Translators: Model verbose name and instance representation, # suitable to be an item in a list. _("{class_name} {instance}").format( class_name=p._meta.verbose_name, instance=p)) msg = _( "Deleting {class_name} {instance} would require " "deleting the following protected related objects: " "{related_objects}").format( class_name=self._meta.model._meta.verbose_name, instance=self.instance, related_objects=get_text_list(objs, _("and")), ) raise ValidationError(msg, code="deleting_protected")
def delete_product(request, pid): product = get_object_or_404(Product, pk=pid) form = DeleteProductForm(instance=product) if request.method == 'POST': if 'id' in request.POST and str(product.id) == request.POST['id']: form = DeleteProductForm(request.POST, instance=product) if form.is_valid(): if product.tags: del product.tags product.delete() messages.add_message(request, messages.SUCCESS, 'Product and relationships removed.', extra_tags='alert-success') create_notification(event='other', title='Deletion of %s' % product.name, description='The product "%s" was deleted by %s' % (product.name, request.user), url=request.build_absolute_uri(reverse('product')), icon="exclamation-triangle") return HttpResponseRedirect(reverse('product')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([product]) rels = collector.nested() product_tab = Product_Tab(pid, title="Product", tab="settings") return render(request, 'dojo/delete_product.html', {'product': product, 'form': form, 'product_tab': product_tab, 'rels': rels, })
def will_be_deleted_with(instance): """Get items that would be deleted along with model ``instance``. Pass in any Django model instance that you intend to delete and get an iterator of related objects that would also be deleted. Since this is implemented as a generator, if you want a list of items, you'll need to do ``list(will_be_deleted_with(instance))``. Args: instance: A Django ORM instance Returns: pairs: (model class, items of that class that will be deleted) """ # XXX: Not sure why this import can't be moved to module scope. from django.contrib.admin.utils import NestedObjects # The collector returns a list of all objects in the database that # would be deleted if `obj` were deleted. collector = NestedObjects(using='default') collector.collect([instance]) for cls, items_to_delete in collector.data.items(): # XXX: Not sure the collector will ever include the original # XXX: instance, but this check was in the original version and # XXX: I don't have time to verify at the moment. if instance in items_to_delete: items_to_delete.remove(instance) if items_to_delete: yield cls, items_to_delete
def delete_test(request, tid): test = get_object_or_404(Test, pk=tid) eng = test.engagement form = DeleteTestForm(instance=test) from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([test]) rels = collector.nested() 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(): del test.tags test.delete() messages.add_message(request, messages.SUCCESS, 'Test and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_engagement', args=(eng.id,))) add_breadcrumb(parent=test, title="Delete", top_level=False, request=request) return render(request, 'dojo/delete_test.html', {'test': test, 'form': form, 'rels': rels, 'deletable_objects': rels, })
def delete_group(request, gid): group = get_object_or_404(Dojo_Group, id=gid) form = DeleteGroupForm(instance=group) if request.method == 'POST': if 'id' in request.POST and str(group.id) == request.POST['id']: form = DeleteGroupForm(request.POST, instance=group) if form.is_valid(): group.delete() messages.add_message( request, messages.SUCCESS, 'Group and relationships successfully removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('groups')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([group]) rels = collector.nested() add_breadcrumb(title="Delete Group", top_level=False, request=request) return render(request, 'dojo/delete_group.html', { 'to_delete': group, 'form': form, 'rels': rels })
def delete_engagement_presets(request, pid, eid): prod = get_object_or_404(Product, id=pid) preset = get_object_or_404(Engagement_Presets, id=eid) form = DeleteEngagementPresetsForm(instance=preset) if request.method == 'POST': if 'id' in request.POST: form = DeleteEngagementPresetsForm(request.POST, instance=preset) if form.is_valid(): preset.delete() messages.add_message( request, messages.SUCCESS, 'Engagement presets and engagement relationships removed.', extra_tags='alert-success') return HttpResponseRedirect( reverse('engagement_presets', args=(pid, ))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([preset]) rels = collector.nested() product_tab = Product_Tab(pid, title="Delete Engagement Preset", tab="settings") return render( request, 'dojo/delete_presets.html', { 'product': product, 'form': form, 'product_tab': product_tab, 'rels': rels, })
def _related_objects(self, using): """ Method to get all objects with foreign key the relation with self instance Args: self Returns: List(): all related objects """ using = using or router.db_for_write(self.__class__, instance=self) collector = NestedObjects(using=using) collector.collect([self]) def parse_list(obj): """ Parse to nested objects Args: obj: list instance or models object instance Returns: list(): list with objects """ if isinstance(obj, list): array = [] for item in obj: array += parse_list(item) return array return [obj] colletion = parse_list(collector.nested()) colletion.remove(self) return colletion
def delete_product(request, pid): product = get_object_or_404(Product, pk=pid) form = DeleteProductForm(instance=product) from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([product]) rels = collector.nested() if request.method == 'POST': if 'id' in request.POST and str(product.id) == request.POST['id']: form = DeleteProductForm(request.POST, instance=product) if form.is_valid(): if product.tags: del product.tags product.delete() messages.add_message(request, messages.SUCCESS, 'Product and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('product')) add_breadcrumb(parent=product, title="Delete", top_level=False, request=request) return render(request, 'dojo/delete_product.html', {'product': product, 'form': form, 'rels': rels, })
def get_deleted_objects(objs, request): 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): opts = obj._meta no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), obj) perms_needed.add(opts.verbose_name) try: return format_html('{}: <a href="{}">{}</a>', capfirst(opts.verbose_name), obj.get_absolute_url(), obj) except AttributeError: 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_endpoint(request, eid): endpoint = get_object_or_404(Endpoint, pk=eid) product = endpoint.product form = DeleteEndpointForm(instance=endpoint) from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([endpoint]) rels = collector.nested() if request.method == 'POST': if 'id' in request.POST and str(endpoint.id) == request.POST['id']: form = DeleteEndpointForm(request.POST, instance=endpoint) if form.is_valid(): del endpoint.tags endpoint.delete() messages.add_message(request, messages.SUCCESS, 'Endpoint and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_product', args=(product.id,))) add_breadcrumb(parent=endpoint, title="Delete", top_level=False, request=request) return render(request, 'dojo/delete_endpoint.html', {'endpoint': endpoint, 'form': form, 'rels': rels, })
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['protected_objects'] = self.protected_objects collector = NestedObjects(using='default') collector.collect([self.object]) to_be_deleted = collector.nested() def remove_unpublished(tree): filtered_tree = [] for item in tree: if type(item) == list: filtered_tree.append(remove_unpublished(item)) elif type(item) == Page: filtered_tree.append(item) elif issubclass(type(item), Block) and type(item) != Block: if item.published: filtered_tree.append(item) elif type(item) == Reference: if item.containing_block.published: filtered_tree.append(item) return filtered_tree context['to_be_deleted'] = remove_unpublished(to_be_deleted) return context
def merge_users(user, user_uuid): existing_user, created = get_user_model().objects.get_or_create( uuid=user_uuid, defaults={'is_active': True}) collector = NestedObjects(using='default') collector.collect([user]) to_change_user = collector.nested() try: related_objects = to_change_user[1] except IndexError: related_objects = [] for related_object in related_objects: if getattr(related_object, 'user', None) == user: related_object.user = existing_user related_object.save() if getattr(related_object, 'created_by', None) == user: related_object.created_by = existing_user related_object.save() for group in user.communication_groups.all(): group.users.add(existing_user) if hasattr(existing_user, 'participant'): participant = existing_user.participant participant.delete() user.delete()
def delete(self, cascade=True, **kwargs): if self.PREVENT_DELETE: raise DeleteNotPermitted() if cascade: collector = NestedObjects(using='default') collector.collect([self]) field_updates = collector.field_updates for cls, to_update in field_updates.iteritems(): for (field, value), instances in to_update.iteritems(): cls.objects.filter( pk__in={o.pk for o in instances} ).update( **{field.attname: value} ) for klass, objs in collector.data.iteritems(): try: klass._meta.get_field('is_void') except models.FieldDoesNotExist: pass else: klass.objects.filter(pk__in={o.pk for o in objs}).update( is_void=True ) else: self.is_void = True self.save() signals.post_delete.send( sender=self.__class__, instance=self )
def delete_github(request, tid): github_instance = get_object_or_404(GITHUB_Conf, pk=tid) # eng = test.engagement # TODO Make Form form = DeleteGITHUBConfForm(instance=github_instance) if request.method == 'POST': if 'id' in request.POST and str(github_instance.id) == request.POST['id']: form = DeleteGITHUBConfForm(request.POST, instance=github_instance) if form.is_valid(): github_instance.delete() messages.add_message(request, messages.SUCCESS, 'Github Conf and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('github')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([github_instance]) rels = collector.nested() add_breadcrumb(title="Delete", top_level=False, request=request) return render(request, 'dojo/delete_github.html', {'inst': github_instance, 'form': form, 'rels': rels, 'deletable_objects': rels, })
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') 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_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(): del test.tags test.delete() messages.add_message(request, messages.SUCCESS, 'Test and relationships removed.', extra_tags='alert-success') 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 delete_jira(request, tid): jira_instance = get_object_or_404(JIRA_Conf, pk=tid) # eng = test.engagement # TODO Make Form form = DeleteJIRAConfForm(instance=jira_instance) if request.method == 'POST': if 'id' in request.POST and str(jira_instance.id) == request.POST['id']: form = DeleteJIRAConfForm(request.POST, instance=jira_instance) if form.is_valid(): 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 URL %s' % jira_instance.url, description='JIRA url "%s" was deleted by %s' % (jira_instance.url, request.user), url=request.build_absolute_uri(reverse('jira')), ) return HttpResponseRedirect(reverse('jira')) 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, })
class DeleteViewWithDependencies(FlagMixin, RulesRequiredMixin, AjaxTemplateMixin, DeleteView): success_message = "The objects are been deleted successfully" protected_template = None collector = None def get(self, request, *args, **kwargs): self.collector = NestedObjects(using="default") self.get_collect() self.post_collect() # If there is some protected objects, change the template if self.collector.protected: self.template_name = self.protected_template return super().get(request, *args, **kwargs) def get_collect(self): # Collect objects how will be deleted self.collector.collect([self.get_object()]) def post_collect(self): pass def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["deletable_objects"] = self.collector.nested(format_callback) context["protected_objects"] = self.collector.protected return context def delete(self, request, *args, **kwargs): result = super().delete(request, *args, **kwargs) display_success_messages(request, _(self.success_message)) return result
def get_deleted_objects(self, objs, request): # we override here to allow soft_delete, modified from # https://github.com/django/django/blob/master/django/contrib/admin/utils.py """ 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) def format_callback(obj): return '%s: %s' % (capfirst(obj._meta.verbose_name), obj) to_delete = collector.nested(format_callback) model_count = {model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items()} # we need to display count by model of the protected items too protected = [format_callback(obj) for obj in collector.protected] protected_model = {obj._meta.verbose_name_plural for obj in collector.protected} protected_model_count = dict(Counter(protected_model)) # since we are only performing soft delete, we may soft delete the protected objects later return to_delete + protected, {**model_count, **protected_model_count}, set(), []
def delete_product_type(request, ptid): product_type = get_object_or_404(Product_Type, pk=ptid) form = Delete_Product_TypeForm(instance=product_type) if request.method == 'POST': if 'id' in request.POST and str(product_type.id) == request.POST['id']: form = Delete_Product_TypeForm(request.POST, instance=product_type) if form.is_valid(): product_type.delete() messages.add_message(request, messages.SUCCESS, 'Product Type and relationships removed.', extra_tags='alert-success') create_notification( event='other', title='Deletion of %s' % product_type.name, no_users=True, description='The product type "%s" was deleted by %s' % (product_type.name, request.user), url=request.build_absolute_uri(reverse('product_type')), icon="exclamation-triangle") return HttpResponseRedirect(reverse('product_type')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([product_type]) rels = collector.nested() add_breadcrumb(title="Delete Product Type", top_level=False, request=request) return render(request, 'dojo/delete_product_type.html', { 'product_type': product_type, 'form': form, 'rels': rels, })
def create_backup(request): _pk = request.POST.get('_id') # user object user_obj = User.objects.get(pk=_pk) # NestedObjects is admin contrib package which is used as a Collector subclass. collector = NestedObjects(using="default") # database name # create an object of NestedObjects collector.collect([user_obj]) # create a list of all objects of all tables with foreign keys objects = list(chain.from_iterable(collector.data.values())) # store a data in file with open("dbfiles/{}.json".format(user_obj.username), "w") as f: s = serializers.serialize("json", objects, use_natural_foreign_keys=True, use_natural_primary_keys=True, indent=4) # make all tables objects pks null # s = re.sub('"pk": [0-9]{1,5}', '"pk": null', s) f.write(s) data = {'msg': 'Backup Created Successfully'} return JsonResponse(data)
def hand_clean_DELETE(self): """ We don't validate the 'DELETE' field itself because on templates it's not rendered using the field information, but just using a generic "deletion_field" of the InlineModelAdmin. """ if self.cleaned_data.get(DELETION_FIELD_NAME, False): using = router.db_for_write(self._meta.model) collector = NestedObjects(using=using) if self.instance.pk is None: return collector.collect([self.instance]) if collector.protected: objs = [] for p in collector.protected: objs.append( # Translators: Model verbose name and instance representation, # suitable to be an item in a list. _('%(class_name)s %(instance)s') % { 'class_name': p._meta.verbose_name, 'instance': p} ) params = {'class_name': self._meta.model._meta.verbose_name, 'instance': self.instance, 'related_objects': get_text_list(objs, _('and'))} msg = _("Deleting %(class_name)s %(instance)s would require " "deleting the following protected related objects: " "%(related_objects)s") raise ValidationError(msg, code='deleting_protected', params=params)
def get_deleted_objects(objs): """Based on `django/contrib/admin/utils.py`""" collector = NestedObjects(using="default") collector.collect(objs) def format_callback(obj): opts = obj._meta # Display a link to the admin page. try: return format_html( '{}: <a href="{}">{}</a>', capfirst(opts.verbose_name), reverse(app_urlname(opts, "update"), kwargs={"pk": obj.pk}), obj, ) except NoReverseMatch: pass no_edit_link = "%s: %s" % (capfirst( opts.verbose_name), force_text(obj)) 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, protected
def get_context_data(self, **kwargs): context = super(Delete, self).get_context_data(**kwargs) context['list_reversed_url'] = reverse_lazy(TRAVEL_LIST_URL_NAME) collector = NestedObjects(using='default') collector.collect([self.get_object()]) context['deleted_objects'] = collector.nested() return context
def _merge_election(Election, old_list): dest_slug = old_list.pop(0) dest_model = None while not dest_model and old_list: try: dest_model = Election.objects.get(slug=dest_slug) except Election.DoesNotExist: # This election doesn't exist, so it might just exist in # the person versions. Because of this, it's safe to return # here and leave the migration to rename the slug in the versions json dest_slug = old_list.pop(0) continue for source_slug in old_list: try: source_model = Election.objects.get(slug=source_slug) except Election.DoesNotExist: # Same as above – if this election doesn't exist than there # is nothing to merge continue source_model.ballot_set.update(election=dest_model) source_model.officialdocument_set.update(election=dest_model) source_model.resultevent_set.update(election=dest_model) collector = NestedObjects(using=connection.cursor().db.alias) collector.collect([source_model]) if len(collector.nested()) > 1: # A related object exist for this source election, # something has gone wrong. print(collector.nested()) raise ValueError( "Can't merge election {} with related objects".format( source_slug)) source_model.delete() return dest_slug
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') 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_user(request, uid): user = get_object_or_404(Dojo_User, id=uid) form = DeleteUserForm(instance=user) from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([user]) rels = collector.nested() if user.id == request.user.id: messages.add_message(request, messages.ERROR, 'You may not delete yourself.', extra_tags='alert-danger') return HttpResponseRedirect(reverse('edit_user', args=(user.id,))) if request.method == 'POST': if 'id' in request.POST and str(user.id) == request.POST['id']: form = DeleteUserForm(request.POST, instance=user) if form.is_valid(): user.delete() messages.add_message(request, messages.SUCCESS, 'User and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('users')) add_breadcrumb(title="Delete User", top_level=False, request=request) return render(request, 'dojo/delete_user.html', {'to_delete': user, 'form': form, 'rels': rels, })
def delete_product(request, pid): product = get_object_or_404(Product, pk=pid) form = DeleteProductForm(instance=product) if request.method == 'POST': if 'id' in request.POST and str(product.id) == request.POST['id']: form = DeleteProductForm(request.POST, instance=product) if form.is_valid(): if product.tags: del product.tags product.delete() messages.add_message(request, messages.SUCCESS, 'Product and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('product')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([product]) rels = collector.nested() product_tab = Product_Tab(pid, title="Product", tab="settings") return render( request, 'dojo/delete_product.html', { 'product': product, 'form': form, 'product_tab': product_tab, 'rels': rels, })
def delete_survey(request, sid): survey = get_object_or_404(Engagement_Survey, id=sid) form = Delete_Eng_Survey_Form(instance=survey) from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([survey]) rels = collector.nested() if request.method == 'POST': if 'id' in request.POST and str(survey.id) == request.POST['id']: form = Delete_Eng_Survey_Form(request.POST, instance=survey) if form.is_valid(): survey.delete() messages.add_message(request, messages.SUCCESS, 'Survey and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('survey')) add_breadcrumb(title="Delete Survey", top_level=False, request=request) return render(request, 'defectDojo-engagement-survey/delete_survey.html', { 'survey': survey, 'form': form, 'rels': rels, })
def delete_sql_data(self): for model_class, queryset in get_querysets_to_dump(self.domain_name, []): collector = NestedObjects(using=queryset.db) collector.collect(queryset) collector.delete() self.assertEqual([], list(get_objects_to_dump(self.domain_name, [])))
def delete_engagement_presets(request, pid, eid): prod = get_object_or_404(Product, id=pid) preset = get_object_or_404(Engagement_Presets, id=eid) form = DeleteEngagementPresetsForm(instance=preset) if request.method == 'POST': if 'id' in request.POST: form = DeleteEngagementPresetsForm(request.POST, instance=preset) if form.is_valid(): preset.delete() messages.add_message(request, messages.SUCCESS, 'Engagement presets and engagement relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('engagement_presets', args=(pid,))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([preset]) rels = collector.nested() product_tab = Product_Tab(pid, title="Delete Engagement Preset", tab="settings") return render(request, 'dojo/delete_presets.html', {'product': product, 'form': form, 'product_tab': product_tab, 'rels': rels, })
def delete_product(request, pid): product = get_object_or_404(Product, pk=pid) form = DeleteProductForm(instance=product) if request.method == 'POST': if 'id' in request.POST and str(product.id) == request.POST['id']: form = DeleteProductForm(request.POST, instance=product) if form.is_valid(): if product.tags: del product.tags product.delete() messages.add_message(request, messages.SUCCESS, 'Product and relationships removed.', extra_tags='alert-success') return HttpResponseRedirect(reverse('product')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([product]) rels = collector.nested() product_tab = Product_Tab(pid, title="Product", tab="settings") return render(request, 'dojo/delete_product.html', {'product': product, 'form': form, 'product_tab': product_tab, 'rels': rels, })
def delete_endpoint(request, eid): endpoint = get_object_or_404(Endpoint, pk=eid) product = endpoint.product form = DeleteEndpointForm(instance=endpoint) if request.method == 'POST': if 'id' in request.POST and str(endpoint.id) == request.POST['id']: form = DeleteEndpointForm(request.POST, instance=endpoint) if form.is_valid(): endpoint.delete() messages.add_message(request, messages.SUCCESS, 'Endpoint and relationships removed.', extra_tags='alert-success') create_notification(event='other', title='Deletion of %s' % endpoint, description='The endpoint "%s" was deleted by %s' % (endpoint, request.user), url=request.build_absolute_uri(reverse('endpoints')), icon="exclamation-triangle") return HttpResponseRedirect(reverse('view_product', args=(product.id,))) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([endpoint]) rels = collector.nested() product_tab = Product_Tab(endpoint.product.id, "Delete Endpoint", tab="endpoints") return render(request, 'dojo/delete_endpoint.html', {'endpoint': endpoint, 'product_tab': product_tab, 'form': form, 'rels': rels, })
def get_deleted_objects(object): """ List the related objects before delete an object """ collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([object]) return collector.nested()
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 if hasattr(obj, '_bmfmeta') and obj._bmfmeta.only_related: return format_html( '{0}: {1}', obj._meta.verbose_name, obj ) else: return format_html( '{0}: <a href="{1}">{2}</a>', obj._meta.verbose_name, obj.bmfmodule_detail(), 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 test_relation_on_abstract(self): """ #21846 -- Check that `NestedObjects.collect()` doesn't trip (AttributeError) on the special notation for relations on abstract models (related_name that contains %(app_label)s and/or %(class)s). """ n = NestedObjects(using=DEFAULT_DB_ALIAS) Car.objects.create() n.collect([Vehicle.objects.first()])
def _nested_objs(obj): c = NestedObjects("default") c.collect([obj]) def format_callback(obj): return mark_safe('<a href="/keys/{}/{}/">{}</a>'.format(obj._meta.model_name, obj.pk, escape(obj))) result = unordered_list(c.nested(format_callback)) return mark_safe("<ul>{}</ul>".format(result))
def list_deleted_objects(obj): model = obj.__class__ def format_callback(item): return u'%s: %s' % (item._meta.verbose_name.capitalize(), force_unicode(item)) collector = NestedObjects(using=router.db_for_write(model)) collector.collect(model.objects.filter(id=obj.id)) to_delete = collector.nested(format_callback) return {'to_delete': to_delete}
def test_on_delete_do_nothing(self): """ Check that the nested collector doesn't query for DO_NOTHING objects. """ n = NestedObjects(using=DEFAULT_DB_ALIAS) objs = [Event.objects.create()] EventGuide.objects.create(event=objs[0]) with self.assertNumQueries(2): # One for Location, one for Guest, and no query for EventGuide n.collect(objs)
def collect_objects(self, objs): ''' Collect all related objects ''' collector = NestedObjects(using=self.using) collector.collect(objs) if self.delete_type == 'soft_delete': collector = self.get_un_soft_deleted_objects(collector) return collector
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_merged_objects(objs, opts, user, admin_site, using): """ Find all objects related to ``objs`` that should also be merged. ``objs`` must be a homogenous iterable of objects (e.g. a QuerySet). Returns a nested list of strings suitable for display in the template with the ``unordered_list`` filter. """ collector = NestedObjects(using=using) collector.collect(objs) perms_needed = set() def format_callback(obj): has_admin = obj.__class__ in admin_site._registry opts = obj._meta if has_admin: admin_url = reverse('%s:%s_%s_change' % (admin_site.name, opts.app_label, opts.object_name.lower()), None, (quote(obj._get_pk_val()),)) p = '%s.%s' % (opts.app_label, opts.get_delete_permission()) if not user.has_perm(p): perms_needed.add(opts.verbose_name) p = '%s.%s' % (opts.app_label, opts.get_change_permission()) if not user.has_perm(p): perms_needed.add(opts.verbose_name) p = '%s.%s' % (opts.app_label, opts.get_add_permission()) if not user.has_perm(p): perms_needed.add(opts.verbose_name) # Display a link to the admin page. return mark_safe(u'%s: <a href="%s">%s</a>' % (escape(capfirst(opts.verbose_name)), admin_url, escape(obj))) else: # Don't display link to edit, because it either has no # admin or is edited inline. return u'%s: %s' % (capfirst(opts.verbose_name), force_text(obj)) to_merge = collector.nested(format_callback) protected = [format_callback(obj) for obj in collector.protected] return to_merge, perms_needed, protected
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_delete_cascade(request, title): document = get_object_or_error(Document, request.user, ['change_document'], url_title=title) 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 get_deleted_objects(objs, using): """ 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 objects suitable for display in the template with the ``unordered_list`` filter. This is simplified from a method by the same name that the Django admin uses. "using" means the key in the DATABASES setting. """ collector = NestedObjects(using=using) collector.collect(objs) # nested() can take a formatting callback if we want it later to_delete = collector.nested() return to_delete
def collect(self): def collect_objects(obj): try: iter(obj) for o in obj: collect_objects(o) except TypeError: flattened.append(obj) collector = NestedObjects(using=self.using) collector.collect(self.qs) objs = collector.nested() flattened = [] for obj in objs: collect_objects(obj) return flattened
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] return to_delete, collector.model_count, perms_needed, protected
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 collect_backlinks(model_instance): """:param model_instance: Django Model Instance :return: A dict of Models that reference the current Useful for determining if an instance can be deleted. Includes hyperlinks to the related models """ from django.contrib.admin.utils import NestedObjects collector = NestedObjects(using='scenario_db') # or specific database collector.collect([model_instance]) # https://docs.djangoproject.com/en/1.7/releases/1.7/#remove-and-clear-methods-of-related-managers dependants = collector.nested() # fun fact: spelling differs between America and Brittain #print("Found related models:", dependants) links = {} if len(dependants[1:]): for direct_reference in dependants[1:][0]: # only iterates over the top level if not isinstance(direct_reference, list) and not isinstance(direct_reference, RelationalPoint): # Points are obvious, don't include them name = direct_reference.__class__.__name__ try: # not everything has a name attr links[str(direct_reference)] = '/setup/%s/%i/' % (name, direct_reference.pk) except: links['%s:%i' % (name, direct_reference.pk)] = '/setup/%s/%i/' % (name, direct_reference.pk) #print(links) return links