Reputation: 155
I have a model which is set as foreign key in several models. Right now deleting any object from the model throws ProtectedError if it is referenced in any of those model. I want to let user delete the object with all protected objects in a single operation.
I can delete the first layer of protected objects by simply calling
....
except ProtectedError as e
e.protected_objects.delete()
....
But when the protected_objects
have their own protected objects, the operation fails and throws another second layer ProtectedError
. What I want to achieve is, deleting all the protected objects undiscriminating in which layer it exists. I am aware that it can be a dangerous operation to perform. But can I achieve this without a complex solution. Thanks in advance.
Source Code, where I am trying to perform the ajax operation:
try:
obj_list = model.objects.filter(pk__in=pk_list)
log_deletion(request, obj_list, message='Record Deleted')
obj_list.delete()
return JsonResponse({'success': True, 'status_message': '{0} record(s) has been deleted successfully.'.format(len(pk_list))})
except ProtectedError as e:
e.protected_objects.delete()
return JsonResponse({'success': False, 'status_message': 'This operation cannot be executed. One or more objects are in use.'})
Upvotes: 2
Views: 1144
Reputation: 114270
In general, you could use a loop:
...
except ProtectedError as e:
obj = e.protected_objects
while True:
try:
obj.delete()
except ProtectedError as e:
obj = e.protected_objects
else:
break
...
To log which layer the errors happen in, you can add a counter:
from itertools import count
obj_list = model.objects.filter(pk__in=pk_list)
for layer in count():
try:
log_deletion(request, obj_list, message='Record Deleted in layer {}'.format(layer))
obj_list.delete()
except ProtectedError as e:
obj_list = e.protected_objects
else:
if layer == 0:
return JsonResponse({'success': True, 'status_message': '{0} record(s) has been deleted successfully.'.format(len(pk_list))})
else:
return JsonResponse({'success': False, 'status_message': 'This operation cannot be executed. One or more objects are in use.'})
Upvotes: 1
Reputation: 48720
It seems like you might not want to use on_delete=models.PROTECT
on the definition of the foreign keys. Have you considered changing the on delete to use CASCADE
instead? If you use cascade, you won't need to iterate over the dependencies to delete them first.
Rather than:
class OtherModel(models.Model):
link = models.ForeignKey("Link", on_delete=models.PROTECT)
You could define the model like:
class OtherModel(models.Model):
link = models.ForeignKey("Link", on_delete=models.CASCADE)
When deleting models from the admin that use CASCADE
an intermediate page will be shown that lists all the dependent objects that will also be deleted.
Upvotes: 2