Переопределенный метод удаления дочерней модели не вызывается при удалении родителя? - PullRequest
6 голосов
/ 20 января 2012

У меня есть две модели, каждая с изображением.У одного есть внешний ключ к родителю.Когда я удаляю родителя, я хочу удалить родителя и потомка вместе с их файлом изображения на диске.Чтобы сделать это, я перезаписываю метод delete:

class MyModelParent(models.Model):
   image = models.ImageField(upload_to = "images/" )

   def delete(self, *args, **kwargs):     
      if self.image:
          self.image.delete()
      super(MyModelParent, self).delete(*args, **kwargs)

class MyModelChild(models.Model):
   parent = models.ForeignKey(MyModelParent)
   image = models.ImageField(upload_to = "images/" )

   def delete(self, *args, **kwargs):     
      if self.image:
          self.image.delete()
      super(MyModelChild, self).delete(*args, **kwargs)

Когда я удаляю экземпляр MyModelParent, вызывается его переопределенная функция delete (), но не дочерние (даже если они удаляются из БД), поэтому их образы остаются на диске.Кто-нибудь знает, что я делаю не так?

Ответы [ 3 ]

10 голосов
/ 20 января 2012

Вы не делаете ничего плохого. Проблема в том, что метод delete() любого потомка не вызывается при каскадировании.

Из документации для delete (каскадное удаление использует запрос к базе данных):

Метод delete () массово удаляет и не вызывает delete () методы на ваших моделях. Это, однако, испускает pre_delete и сигналы post_delete для всех удаленных объектов (включая каскадные делеции).

Однако сигналы pre_delete и post_delete по-прежнему отправляются. Что вам нужно сделать, это подключить обратный вызов, который будет прослушивать один из этих сигналов и выполнять любую дополнительную необходимую очистку. См. Соответствующую документацию для получения дополнительной информации о connecting signals.

2 голосов
/ 20 января 2012

Это связано с тем, что (из Создание запросов | Удаление объектов , в документе) опция CASCADE для ForeignKey 'on_delete Аргумент ключевого слова (который используется по умолчанию) просто устанавливает ограничение базы данных:

Имейте в виду, что это, когда это возможно, будет выполняться исключительно в SQL, и поэтому delete() методы отдельных экземпляров объекта не обязательно будут вызываться во время процесса. Если вы предоставили пользовательский метод delete() для класса модели и хотите, чтобы он вызывался, вам нужно будет «вручную» удалить экземпляры этой модели (например, путем итерации по QuerySet и вызова delete() для каждого объекта в отдельности) вместо использования delete() метода QuerySet.

Когда Django удаляет объект, по умолчанию он эмулирует поведение ограничения SQL ON DELETE CASCADE - другими словами, любые объекты, имеющие внешние ключи, указывающие на объект, который будет удален, будут удалены вместе с ним.

1 голос
/ 20 января 2012

@ Ответ Нейта охватывает часть проблемы, но только часть низкого уровня.Удаление родителя должно привести к тому, что дочерний элемент (который имеет неявный внешний ключ к родительскому элементу) также будет удален в каскаде, но в этом сценарии дочерний метод delete () не запускается.

Все правильно,но что более важно, это потому, что вы удаляете родителя, а не ребенка.В наследовании родитель ничего не знает о методах дочернего элемента, поэтому, насколько это возможно, они также могут не существовать и, следовательно, игнорироваться.Если вы хотите, чтобы пользовательский дочерний метод выполнялся, вы должны запускать его от дочернего, а не родительского метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...