52đź‘Ť
The delete() method does a bulk delete and does not call any delete() methods on your models. It does, however, emit the pre_delete and post_delete signals for all deleted objects (including cascaded deletions).
For that to work, you can override delete method on QuerySet
, and then apply that QuerySet
as manager:
class ImageQuerySet(models.QuerySet):
def delete(self, *args, **kwargs):
for obj in self:
obj.img.delete()
super(ImageQuerySet, self).delete(*args, **kwargs)
class Image(models.Model):
objects = ImageQuerySet.as_manager()
img = models.ImageField(upload_to=get_image_path)
...
def delete(self, *args, **kwargs):
self.img.delete()
super(Image, self).delete(*args, **kwargs)
8đź‘Ť
Delete method of queryset works directly on the database. It does not call Model.delete()
methods. From the docs:
Keep in mind that this will, whenever possible, be executed purely in SQL, and so the delete() methods of individual object instances will not necessarily be called during the process. If you’ve provided a custom delete() method on a model class and want to ensure that it is called, you will need to “manually” delete instances of that model (e.g., by iterating over a QuerySet and calling delete() on each object individually) rather than using the bulk delete() method of a QuerySet.
If you want to override Django administration interface’s default behavior, you can write a custom delete
action:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/actions/
Another method is to override post_delete
(or pre_delete
) signal instead of delete
method:
https://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.post_delete
Like pre_delete, but sent at the end of a model’s delete() method and a queryset’s delete() method.
- [Django]-AWS Cognito as Django authentication back-end for web site
- [Django]-Django user_passes_test decorator
- [Django]-Django content types – how to get model class of content type to create a instance?
8đź‘Ť
I believe this issue is addressed in the docs
where it says:
Overridden model methods are not called on bulk operations
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.
Unfortunately, there isn’t a workaround when creating or updating objects in bulk, since none of save(), pre_save, and post_save are called.
As suggested in the docs above, I believe a better solution is to use the post_delete
signal, like so:
from django.db.models.signals import post_delete
from django.dispatch import receiver
class Image(models.Model):
img = models.ImageField(upload_to=get_image_path)
...
@receiver(post_delete, sender=Image)
def delete_image_hook(sender, instance, using, **kwargs):
instance.img.delete()
Unlike overriding the delete
method, the delete_image_hook
function should be called on bulk deletes and cascading deletes as well. Here is more information on using Django’s Signals: https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-to-signals-sent-by-specific-senders
Note on previous answers:
Some of the earlier posts suggest overriding the delete
method of QuerySet, which may have performance implications and other unintended behavior. Perhaps those answers were written before Django’s Signals were implemented, but I think using Signals is a cleaner approach.
- [Django]-Turn off caching of static files in Django development server
- [Django]-Django: display time it took to load a page on every page
- [Django]-Django data migration when changing a field to ManyToMany
0đź‘Ť
The accepted answer may not work for everyone. I couldn’t get it to work on Django 3.2, but it may only be because I already had a custom manager and was not confident that I could combine my customizations to a models.Manager with customizations to a models.QuerySet.
I found that overriding delete_queryset (available in Django 2.1+) on the model’s admin (as described in this thorough and fully-illustrated answer from user Kushan Gunasekera to another related SO question) was quick and easy.
- [Django]-How to import csv data into django models
- [Django]-Get all related Django model objects
- [Django]-How to override the default value of a Model Field from an Abstract Base Class