Есть очень деликатный момент реализации, который я решил добавить к этому обсуждению.
Допустим, у нас есть две модели, одна из которых ссылается на другую по внешнему ключу, например:
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, который попытается удалить уже удаленный элемент!
Хитрость в том, что для правильной реализации обратного каскадирования требуется ИСКЛЮЧЕНИЕ:
def delete_reverse(sender, **kwargs):
try:
if kwargs['instance'].a:
kwargs['instance'].a.delete()
except:
pass
Этот код будет работать в любом случае. Я надеюсь, что это помогает некоторым людям.