Переопределить метод удаления модели django для группового удаления
Я переопределяю метод удаления модели Django, чтобы удалить файлы-сироты на диске для полей изображения, примерно так:
class Image(models.Model):
img = models.ImageField(upload_to=get_image_path)
...
def delete(self, *args, **kwargs):
self.img.delete()
super(Image, self).delete(*args, **kwargs)
Это отлично работает, когда я удаляю отдельные объекты из администратора, но когда я выбираю несколько объектов и удаляю их, это, похоже, не вызвано. Я давно искал поисковые запросы, но не использовал правильные ключевые слова, чтобы получить ответ на этот вопрос, и официальная документация, похоже, не говорит об этой теме.
Ответы
Ответ 1
Он делает:
Метод delete() делает массовое удаление и не вызывает никаких методов delete() на ваших моделях. Однако он излучает сигналы pre_delete и post_delete для всех удаленных объектов (включая каскадные удаления).
Для этого вы можете переопределить метод удаления на QuerySet
, а затем применить этот QuerySet
как менеджер:
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)
Ответ 2
Метод удаления queryset работает непосредственно в базе данных. Он не вызывает Model.delete()
. Из документов:
Имейте в виду, что это, по возможности, будет выполняться исключительно в SQL, и поэтому методы delete() отдельных экземпляров объекта не обязательно будут вызываться во время процесса. Если вы предоставили пользовательский метод delete() для класса модели и хотите убедиться, что он вызывается, вам нужно будет "вручную" удалить экземпляры этой модели (например, путем итерации по QuerySet и вызова delete() для каждого объекта). индивидуально) вместо использования метода массового удаления() объекта QuerySet.
Если вы хотите переопределить поведение интерфейса администрирования Django по умолчанию, вы можете написать собственное действие delete
:
https://docs.djangoproject.com/en/1.7/ref/contrib/admin/actions/
Другой метод - переопределить post_delete
(или pre_delete
) вместо метода delete
:
https://docs.djangoproject.com/en/1.7/ref/signals/#django.db.models.signals.post_delete
Как и pre_delete, но отправляется в конце метода delete() моделей и метода delete() наборов запросов.
Ответ 3
Я считаю, что эта проблема решена в документации
где сказано:
Переопределенные методы модели не вызываются в массовых операциях
Обратите внимание, что метод delete() для объекта не обязательно вызывается при массовом удалении объектов с использованием QuerySet или в результате каскадного удаления. Чтобы обеспечить выполнение настроенной логики удаления, вы можете использовать сигналы pre_delete и/или post_delete.
К сожалению, нет обходного пути при массовом создании или обновлении объектов, так как не вызывается ни один из методов save(), pre_save и post_save.
Как показано в приведенных выше документах, я считаю, что лучшим решением будет использование сигнала post_delete
, например:
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()
В отличие от переопределения метода delete
, функция delete_image_hook
должна вызываться при массовом удалении, а также каскадном удалении. Вот дополнительная информация об использовании сигналов Django: https://docs.djangoproject.com/en/1.11/topics/signals/#connecting-to-signals-sent-by-specific-senders
Примечание к предыдущим ответам. В некоторых предыдущих публикациях предлагается переопределить метод delete
QuerySet, что может повлиять на производительность и другое непреднамеренное поведение. Возможно, эти ответы были написаны до внедрения Django Signals, но я думаю, что использование Signals - более чистый подход.