1

I have a common field is_active = models.BooleanField() on all my models.

I want to create a universal override to the delete function so that instead of removing records from the database, is_active = False.

I understand the best way to do this is a pre_delete signal rather than overriding delete() itself as delete() is not called in bulk operations.

I have tried the following implementation:

@receiver(pre_delete)
def delete_obj(sender, instance, **kwargs):
    """Override delete() to set object to inactive."""

    return instance.is_active == False

However this still results in objects being deleted from the database. I assume this is because delete() is still called after pre_delete. How do I correct this?

From the docs:

Note that the delete() method for an object is not necessarily called when deleting objects in bulk using a QuerySet or as a result of a cascading delete. To ensure customized delete logic gets executed, you can use pre_delete and/or post_delete signals.

alias51
  • 8,178
  • 22
  • 94
  • 166
  • I think the only possible way is to override the `delete` function. you may also want to consider the case if the deletion is cascaded by the means of relationships. – adnanmuttaleb Jan 05 '20 at 19:01
  • @adnanmuttaleb thanks but that won't help if I am bulk deleting items in a queryset because `delete()` is not called then. – alias51 Jan 05 '20 at 19:02
  • 1
    It is possible to use pre_delete but instead of reinventing the wheel you could use something like https://django-model-utils.readthedocs.io/en/latest/managers.html#softdeletablemanager – iklinac Jan 05 '20 at 19:03
  • @iklinac thanks that's helpful, if possible I'd like to understand the reason behind this problem and the code/syntax to fix it. – alias51 Jan 05 '20 at 19:05
  • @alias51 for bulk operation you can iterate over your queryset and manually calling your custom `delete` method. This is of-course less performant, but it will preserve data integrity. – adnanmuttaleb Jan 05 '20 at 19:07
  • Does this answer your question? [Overriding QuerySet.delete() in Django](https://stackoverflow.com/questions/6459616/overriding-queryset-delete-in-django) – adnanmuttaleb Jan 05 '20 at 19:10
  • @adnanmuttaleb Unfortunately I don't see how it applies. It doesn't reference why `post_delete()` can't interrupt `delete()` or how I would go about doing that. – alias51 Jan 05 '20 at 19:13
  • @alias51 check this https://stackoverflow.com/questions/6459616/overriding-queryset-delete-in-django. – adnanmuttaleb Jan 05 '20 at 19:14
  • @adnanmuttaleb Thanks but that's the same link? – alias51 Jan 05 '20 at 19:26

1 Answers1

0

You want to replace the logic of original method (in this case, perform update instead of delete) not to extend it.

Signals are to extend the main method logic, not replace it.

In pre_ signals - run some additional logic before the action (maybe even modify the object). In post_ signals - run some additional logic after the main action. But the main action is still run - and it should - it was called rightfully in the code - signals are not supposed to replace it.

Like extending method on the model: you will add some logic before or after calling super().method() - that is how signals work.

In case of replacing method logic with another one - when overriding the method super() most probably will not be called so no original actions be performed.


So, to replace delete() with custom logic you can override both model's delete() method and use custom QuerySet for model with overriden delete() method so when calling delete() either on model instance or QuerySet - it will perform update instead.


Link to surce code where QuerySet delete is performed:

  • pre_delete signal is sent
  • delete_batch is performed
  • post_delete signal is sent
Oleg Russkin
  • 4,234
  • 1
  • 8
  • 20