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, })
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_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 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 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_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(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_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_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_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 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 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 _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 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 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 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 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 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(): 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, 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 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(): 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") 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 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) 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 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, )
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
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_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_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 _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 _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_user(request, uid): user = get_object_or_404(Dojo_User, id=uid) form = DeleteUserForm(instance=user) 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(): try: user.delete() messages.add_message(request, messages.SUCCESS, 'User and relationships removed.', extra_tags='alert-success') except RestrictedError as err: messages.add_message( request, messages.WARNING, 'User cannot be deleted: {}'.format(err), extra_tags='alert-warning') return HttpResponseRedirect(reverse('users')) collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([user]) rels = collector.nested() 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_rule(request, tid): rule = get_object_or_404(Rule, pk=tid) form = DeleteRuleForm(instance=rule) if request.method == 'POST': # print('id' in request.POST, file=sys.stderr) # print(str(rule.id) == request.POST['id'], file=sys.stderr) # print(str(rule.id) == request.POST['id'], file=sys.stderr) # if 'id' in request.POST and str(rule.id) == request.POST['id']: form = DeleteRuleForm(request.POST, instance=rule) # print(form.is_valid(), file=sys.stderr) # print(form.errors, file=sys.stderr) # print(form.non_field_errors(), file=sys.stderr) # print('id' in request.POST, file=sys.stderr) 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 delete_sql_data(test, models, domain): for model in models: filters = get_model_domain_filters(model, domain) for filter in filters: collector = NestedObjects(using='default') collector.collect(model.objects.filter(filter)) collector.delete() test.assertFalse(model.objects.filter(filter).exists(), model)
def collect_deleted_objects(modeladmin, request, queryset): """Collects objects that are related to queryset items and checks their permissions. This method checks if the user has permissions to delete items that are anyhow related to theese in the queryset (regardless of the depth). ``modeladmin`` is expected to be a ModelAdmin instance corresponding to the class of items contained in the ``queryset``. """ db_backend = router.db_for_write(queryset.first().__class__) # NestedObjects is undocumented API, can blow up at any time collector = NestedObjects(using=db_backend) collector.collect(queryset) # Check permissions and return a human-readable string representation perms_needed = set() def format_callback(obj): admin_site = modeladmin.admin_site has_admin = obj.__class__ in admin_site._registry opts = obj._meta if has_admin: model_admin = admin_site._registry[obj.__class__] if not request.user.is_superuser and \ not model_admin.has_delete_permission(request, obj): perms_needed.add(opts.verbose_name) return '%s: %s' % (capfirst(force_text(opts.verbose_name)), force_text(obj)) # Get a nested list of dependent objects to_delete = collector.nested(format_callback) protected = [format_callback(obj) for obj in collector.protected] return to_delete, perms_needed, protected
def collect_deleted_objects(modeladmin, request, queryset): """Collects objects that are related to queryset items and checks their permissions. This method checks if the user has permissions to delete items that are anyhow related to theese in the queryset (regardless of the depth). ``modeladmin`` is expected to be a ModelAdmin instance corresponding to the class of items contained in the ``queryset``. """ db_backend = router.db_for_write(queryset.first().__class__) # NestedObjects is undocumented API, can blow up at any time collector = NestedObjects(using=db_backend) collector.collect(queryset) # Check permissions and return a human-readable string representation perms_needed = set() def format_callback(obj): admin_site = modeladmin.admin_site has_admin = obj.__class__ in admin_site._registry opts = obj._meta if has_admin: model_admin = admin_site._registry[obj.__class__] if not request.user.is_superuser and \ not model_admin.has_delete_permission(request, obj): perms_needed.add(opts.verbose_name) return '%s: %s' % (capfirst(opts.verbose_name), force_text(obj)) # Get a nested list of dependent objects to_delete = collector.nested(format_callback) protected = [format_callback(obj) for obj in collector.protected] return to_delete, perms_needed, 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') 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_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: %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')) 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_sql_data(self): for model_class, builder in get_model_iterator_builders_to_dump( self.domain_name, []): for iterator in builder.querysets(): collector = NestedObjects(using=iterator.db) collector.collect(iterator) collector.delete() self.assertEqual([], list(get_objects_to_dump(self.domain_name, [])))
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, ))) 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 duplicate(obj, value=None, field=None, duplicate_order=None): """ Duplicate all related objects of obj setting field to value. If one of the duplicate objects has an FK to another duplicate object update that as well. Return the duplicate copy of obj. duplicate_order is a list of models which specify how the duplicate objects are saved. For complex objects this can matter. Check to save if objects are being saved correctly and if not just pass in related objects in the order that they should be saved. """ collector = NestedObjects(using=DEFAULT_DB_ALIAS) collector.collect([obj]) collector.sort() related_models = list(collector.data.keys()) data_snapshot = {} for key in list(collector.data.keys()): data_snapshot.update({ key: dict( list( zip([item.pk for item in collector.data[key]], [item for item in collector.data[key]]))) }) root_obj = None if duplicate_order is None: duplicate_order = reversed(related_models) for model in duplicate_order: # Find all FKs on model that point to a related_model. fks = [] for f in model._meta.fields: if isinstance(f, ForeignKey) and f.rel.to in related_models: fks.append(f) # Replace each `sub_obj` with a duplicate. if model not in collector.data: continue sub_objects = collector.data[model] for obj in sub_objects: for fk in fks: fk_value = getattr(obj, "%s_id" % fk.name) # If this FK has been duplicated then point to the duplicate. fk_rel_to = data_snapshot[fk.rel.to] if fk_value in fk_rel_to: dupe_obj = fk_rel_to[fk_value] setattr(obj, fk.name, dupe_obj) # Duplicate the object and save it. obj.id = None if field is not None: setattr(obj, field, value) obj.save() if root_obj is None: root_obj = obj return root_obj
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_endpoint', args=(product.id, ))) 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 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 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 delete_sql_data(self): for model_class, builder in get_model_iterator_builders_to_dump(self.domain_name, []): for iterator in builder.querysets(): with transaction.atomic(using=iterator.db), \ constraint_checks_deferred(iterator.db): collector = NestedObjects(using=iterator.db) collector.collect(iterator) collector.delete() self.assertEqual([], list(get_objects_to_dump(self.domain_name, [])))
def delete_domain_sql_data_for_dump_load_test(domain_name): for model_class, builder in get_model_iterator_builders_to_dump(domain_name, []): for iterator in builder.querysets(): with transaction.atomic(using=iterator.db), \ constraint_checks_deferred(iterator.db): collector = NestedObjects(using=iterator.db) collector.collect(iterator) collector.delete() assert [] == list(get_objects_to_dump(domain_name, [])), "Not all SQL objects deleted"
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 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_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 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 setUp(self): self.n = NestedObjects(using=DEFAULT_DB_ALIAS) self.objs = [Count.objects.create(num=i) for i in range(5)]
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 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()])