开发者

In django, how to delete all related objects when deleting a certain type of instances?

开发者 https://www.devze.com 2023-03-25 09:00 出处:网络
I first tried to override the delete() method but that doesn\'t work for QuerySet\'s bulk delete method. It should be related to 开发者_运维知识库pre_delete signal but I can\'t figure it out. My code

I first tried to override the delete() method but that doesn't work for QuerySet's bulk delete method. It should be related to 开发者_运维知识库pre_delete signal but I can't figure it out. My code is as following:

def _pre_delete_problem(sender, instance, **kwargs):
    instance.context.delete()
    instance.stat.delete()

But this method seems to be called infinitely and the program runs into a dead loop. Can someone please help me?


If the class has foreign keys (or related objects) they are deleted by default like a DELETE CASCADE in sql.

You can change the behavior using the on_delete argument when defining the ForeignKey in the class, but by default it is CASCADE. You can check the docs here.

Now the pre_delete signal works, but it doesn't call the delete() method if you are using a bulk delete, since its not deleting in a object by object basis.


In your case, using the post_delete signal instead of pre_delete should fix the infinite loop issue. Due to a ForeignKey's on_delete default value of cascade, using pre_delete logic this way will trigger the instance.context object to call delete on instance, which will then call instance.context, and so forth.

Using this approach:

def _post_delete_problem(sender, instance, **kwargs):
    instance.context.delete()
    instance.stat.delete()

post_delete.connect(_post_delete_problem, sender=Foo)

Can do the cleanup you want.


If you'd like a quick one-off to delete an instance and all of its related objects and those related objects' objects and so on without having to change the DB schema, you can do this -

def recursive_delete(to_del):
    """Recursively delete an object, all of its protected related
    instances, those instances' protected instances, and so on.
    """
    from django.db.models import ProtectedError
    while True:
        try:
            to_del_pk = to_del.pk
            if to_del_pk is None:
                return  # unsaved object
            to_del.delete()
            print(f"Deleted {to_del.__class__.__name__} with pk {to_del_pk}: {to_del}")
        except ProtectedError as e:
            for protected_ob in e.protected_objects:
                recursive_delete(protected_ob)

Be careful, though!

I'd only use this to help with debugging in one-off scripts (or on the shell) with test databases that I don't mind wiping. Relationships aren't always obvious and if something is protected, it's probably for a reason.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号