Удаление каскада Django на внешних внешних ключах

Django показывает, как устанавливать или переопределять каскадные удаления с помощью внешних ключей в своих документах.

model = models.ForeignKey(MyModel, null = True, on_delete = models.SET_NULL)

Но что, если мы хотим, чтобы этот эффект был наоборот? Что, если мы хотим, чтобы удаление fk-модели привело к удалению этой модели?

Спасибо

Ответ 1

Существует очень тонкая точка реализации, и я думал, что должен добавить к этому обсуждению.

Скажем, у нас есть две модели, одна из которых ссылается на другую с помощью внешнего ключа, например:

class A(models.Model):
    x = models.IntegerField()

class B(models.Model):
    a = models.ForeignKey(A, null=True, blank=True)

Теперь, если мы удалим запись A, каскадное поведение приведет к удалению ссылки в B.

До сих пор так хорошо. Теперь мы хотим изменить это поведение. Очевидным способом, который люди упоминали, является использование сигналов, излучаемых во время удаления, поэтому мы идем:

def delete_reverse(sender, **kwargs):
    if kwargs['instance'].a:
        kwargs['instance'].a.delete()

post_delete.connect(delete_reverse, sender=B)

Это кажется совершенным. Это даже работает! Если мы удалим запись B, соответствующий A также будет удален.

ПРОБЛЕМА заключается в том, что это имеет циклическое поведение, которое вызывает исключение: если мы удалим элемент из A из-за каскадного поведения по умолчанию (которое мы хотим сохранить), соответствующий элемент B также будет удален, что приведет к вызову delete_reverse, который пытается удалить уже удаленный элемент!

Фокус в том, что для правильной реализации обратного каскадирования требуется EXCEPTION HANDLING:

def delete_reverse(sender, **kwargs):
    try:
        if kwargs['instance'].a:
            kwargs['instance'].a.delete()
    except:
        pass

Этот код будет работать в любом случае. Надеюсь, это поможет некоторым людям.

Ответ 2

Я не думаю, что функция, на которую вы смотрите, представляет собой концепцию ORM или базы данных. Вы просто хотите выполнить обратный вызов, когда что-то будет удалено.

Поэтому используйте post_delete signal и добавьте туда обработчик обратного вызова

from django.db.models.signals import post_delete
from django.dispatch import receiver
from myapp.models import MyModel

@receiver(post_delete, sender=MyModel)
def my_post_delete_callback(sender, **kwargs):
    #Sender is the model which when deleted should trigger this action
    #Do stuff like delete other things you want to delete
    #The object just deleted can be accessed as kwargs[instance]