Django - 级联的反向等价物是什么?

时间:2011-03-03 19:34:03

标签: python django

我正在写一个小型音乐数据库。在我过去学习SQL已经有很长一段时间了,我总是想尝试一下django。但是有一件事我无法解决。

目前,我的模型只包含两个类AlbumSongSong有一个指向其所属相册的外键。现在,如果我删除Album,则属于它的所有Song s“将因级联效应而被删除。

相册在我的数据库中是虚拟的,只有歌曲实际上是在文件系统上显示的,并且相册是根据歌曲标签构建的,因此我只能知道如果没有更多的歌曲指向一个专辑就不存在了它(因为它们不再存在于文件系统中)。

或者简而言之,我怎样才能实现反向级联,也就是说,如果没有更多的歌曲指向专辑,那么该专辑​​也应该被删除?

4 个答案:

答案 0 :(得分:4)

删除歌曲并且没有更多歌曲时,您可以使用pre-delete信号删除相册。

from yourapp.models import Album, Song
from django.db.models.signals import pre_delete

def delete_parent(sender, **kwargs):
    # Here you check if there are remaining songs.
    ....

pre_delete.connect(delete_parent, sender=Song)

答案 1 :(得分:1)

我遇到了类似的问题,我最终在相册中添加了一个计数器。如果计数为0且操作为delete(),则相册对象为delete()d。

其他解决方案是在歌曲模型中重载delete()方法,或使用post-delete删除相册。

答案 2 :(得分:0)

Django 1.3将提供定制级联行为的能力[1],但据我所知,仍然没有办法实现你自动描述的内容。

可能最好的方法是为post_delete类编写Song信号处理程序[2],检查相关的Album是否还有Song个如果合适,删除它。尽管已从数据库中删除了ForeignKey,但我认为instance参数中仍应存在Song值。 (如果没有,请尝试使用pre_delete

或者你可以覆盖delete方法[3],但这并不总是被调用,具体取决于你删除对象的方式。

[1] http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.on_delete

[2] http://docs.djangoproject.com/en/dev/ref/signals/#django.db.models.signals.post_delete

[3] http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.delete

答案 3 :(得分:0)

有一个非常微妙的实施点,我认为我应该加入这个讨论。

假设我们有两个模型,其中一个模型通过外键引用另一个模型,如:

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

此代码将以任何方式工作。我希望它可以帮助一些人。