22

In the /admin/ section of Django you can choose to delete items.

If any related objects would be deleted along with your chosen object you get to see a list of affected objects before you confirm delete.

Can I check this in my own function programmatically?

I'd like to do something like

for item in Item.objects.all():
    if not deletion_would_also_delete_other_objects(item):
        item.delete()
Bohr
  • 2,086
  • 3
  • 15
  • 19
  • 4
    Possible duplicate of [How to show related items using DeleteView in Django?](http://stackoverflow.com/questions/12158714/how-to-show-related-items-using-deleteview-in-django) – tutuDajuju Nov 09 '16 at 09:24

2 Answers2

28

Could you use from django.db.models.deletion import Collector to do this?

from django.db.models.deletion import Collector
from .models import Item
  
for item in Item.objects.all(): 
    collector = Collector(using='default')
    collector.collect([item])

    # dependencies should be an empty dict if the item is not related to anything
    if not collector.dependencies:
        item.delete()
JCJS
  • 3,031
  • 3
  • 19
  • 25
Tareq
  • 788
  • 6
  • 8
  • 16
    I get `TypeError: hasattr(): attribute name must be string` on `collector.collect([item])` in Django 1.9. This is fixed if I specify the `using` parameter when instantiating the Collector: `collector = Collector(using='default')`. – Keith Mar 08 '17 at 14:28
  • 3
    I'm not sure this is accurate (at least anymore). I found `.dependencies` was empty, but `.data` and `.fast_deletes` both showed lots of objects that would be deleted. – Chris Withers Aug 01 '18 at 10:46
  • Indeed, both pieces of information from the previous 2 comments should be included in the answer. – Chris Nov 22 '19 at 14:32
  • According to the comments in the code [https://github.com/django/django/blob/master/django/db/models/deletion.py], `.fast_deletes` only includes things that do not cascade, and it looks like `.data` just contains the values of the collected objects themselves. so i think the post as updated is correct. – Personman Nov 17 '20 at 21:34
11

I suggest using the NestedObjects util provided in django admin.

from django.contrib.admin.utils import NestedObjects
from django.db import router

using = router.db_for_write(Item._meta.model)
# if you only have one database, just set using = "default"

nested_object = NestedObjects(using)
nested_object.collect([Item])
# If you want to delete multi item, you can use:
# nested_object.collect(Model.objects.filter(type="deleted"))

print(nested_object.nested()

The result look like this: example result

ramwin
  • 5,803
  • 3
  • 27
  • 29
  • I find this the most convenient way of getting the desired result, because all data is aggregated in one place, and it's much easier to parse a nested list, than several independent dicts (as when using `Collector`). – vakorol Oct 06 '22 at 08:24