Как переопределить метод delete() в модели, чтобы он по-прежнему работал со связанными удалениями

У меня проблема, потому что я удаляю виджет с помощью some_widget_instance.delete(). У меня также есть модель под названием WidgetFile с переопределением метода delete(), так что я могу удалять файлы с жесткого диска при удалении WidgetFile. Проблема, с которой я сталкиваюсь, заключается в том, что если я удалю виджет, и у него есть WidgetFiles, связанные с ним следующим образом:

class WidgetFile(models.Model):

    widget = models.ForeignKey(Widget)

Что ж, когда я удаляю этот виджет, его WidgetFiles удаляются, но метод delete() не срабатывает и не выполняет дополнительные действия на жестком диске. Любая помощь горячо приветствуется.


person orokusaki    schedule 08.10.2009    source источник
comment
Эта проблема возникла из-за того, что когда виджет удаляется, он не запускает метод delete() для каждого из его зависимых элементов (классов, имеющих ссылку на внешний ключ). Он просто удаляет связанные объекты из БД. Это делает его более эффективным, но, очевидно, приводит к таким проблемам.   -  person orokusaki    schedule 17.12.2009


Ответы (8)


Я делаю то же самое и заметил в документах Django самородок, о котором вам следует подумать.

Переопределение предопределенных методов модели

Переопределение Delete Обратите внимание, что метод delete() для объекта не обязательно вызывается при массовом удалении объектов с помощью QuerySet. Чтобы обеспечить выполнение пользовательской логики удаления, вы можете использовать сигналы pre_delete и/или post_delete.

Это означает, что ваш фрагмент не будет всегда делать то, что вы хотите. Использование Сигналы — лучший вариант для удаления.

Я пошел со следующим:

import shutil
from django.db.models.signals import pre_delete 
from django.dispatch import receiver

@receiver(pre_delete)
def delete_repo(sender, instance, **kwargs):
    if sender == Set:
        shutil.rmtree(instance.repo)
person ElementalVoid    schedule 01.10.2012
comment
Что такое Set? модель, которую вы отслеживаете при удалении? - person John Wang; 07.11.2019

Я понял. Я просто поставил это на эту модель виджета:

def delete(self):
    files = WidgetFile.objects.filter(widget=self)
    if files:
        for file in files:
            file.delete()
    super(Widget, self).delete()

Это вызвало необходимый метод удаления() для каждого из связанных объектов, тем самым запустив мой пользовательский код удаления файла. Да, это более дорого для базы данных, но когда вы все равно пытаетесь удалить файлы на жестком диске, это не такая большая затрата, чтобы несколько раз попасть в базу данных.

person orokusaki    schedule 08.10.2009
comment
Непонятно, почему вы думаете, что Celery или cron не столкнутся с подобной ситуацией, когда удаляемый файл может быть уже открыт для операции чтения/записи из другого процесса. В любом случае вам придется кодировать для обработки особого случая. - person Joseph Paetz; 11.12.2013
comment
Я удалил этот фрагмент... 4,5 года назад я подумал, что это может быть хорошей идеей, но я не совсем уверен, почему. - person orokusaki; 31.03.2014

Использование clear() перед удалением удаляет все объекты из связанного набора объектов.

см. django-following-relationships-backward

пример:

group.link_set.clear() 
group.delete() 
person panchicore    schedule 08.10.2009

Это имеет смысл только в том случае, если один Widget точно подключен к одному WidgetFile. В этом случае вы должны использовать OneToOneField< /а>

из примеры "один к одному":

# Delete the restaurant; the waiter should also be removed
>>> r = Restaurant.objects.get(pk=1)
>>> r.delete()
person vikingosegundo    schedule 08.10.2009
comment
действительно верно, но Django выполняет массовое удаление на уровне базы данных для всех ожидающих, не вызывая каждый из их методов удаления, что менее затратно, но и менее традиционно. - person orokusaki; 08.10.2009

Просто чтобы обойти эту проблему: предварительно удалить сигнал. (Ни в коем случае не подразумевается, что реального решения нет.)

person che    schedule 08.10.2009


Является ли some_widget_instance и экземпляром Widget или WidgetFile? Потому что, если это экземпляр Widget, он не получит вашу пользовательскую функцию delete(), которая находится в классе WidgetFile.

person thornomad    schedule 08.10.2009

Начиная с Django 1.9, если вы просто определите on_delete=models.CASCADE для поля, оно удалит все связанные объекты при удалении.

person CLTanuki    schedule 08.04.2016
comment
Это неправильно. Вопрос не в каскадных удалениях. Речь идет об обеспечении вызова связанных delete методов. - person orokusaki; 09.04.2016