【问题标题】:Django - Cascade deletion in ManyToManyRelationDjango - ManyToManyRelation 中的级联删除
【发布时间】:2021-06-27 09:40:12
【问题描述】:

使用以下相关模型(一篇博文可以有多个修订版):

class BlogEntryRevision(models.Model):
    revisionNumber = models.IntegerField()
    title = models.CharField(max_length = 120)
    text = models.TextField()
    [...]

class BlogEntry(models.Model):
    revisions = models.ManyToManyField(BlogEntryRevision)
    [...]

当相应的BlogEntry 被删除时,如何告诉 Django 删除所有相关的BlogEntryRevisions?如果“另一方”的对象被删除,默认值似乎是使对象保持多对多关系。有什么方法可以做到这一点 - 最好不要覆盖BlogEntry.delete

【问题讨论】:

    标签: django many-to-many cascading-deletes


    【解决方案1】:

    我认为您误解了多对多关系的性质。您谈到“相应的 BlogEntry”被删除。但是,ManyToMany 的全部意义在于每个 BlogEntryRevision 都有 multiple 与之相关的 BlogEntries。 (当然,每个 BlogEntry 都有多个 BlogEntryRevision,但您已经知道了。)

    根据您使用的名称,以及您想要这种删除级联功能的事实,我认为您最好使用从 BlogEntryRevision 到 BlogEntry 的标准 ForeignKey。只要你不在那个 ForeignKey 上设置null=True,删除就会级联 - 当 BlogEntry 被删除时,所有的 Revisions 也会被删除。

    截至 Django 2.0

    ForeignKey 初始化程序现在要求您指定 on_delete 参数:

    from django.db import models
    from .models import MyRelatedModel
    
    
    class model(models.Model):
        related_model = models.ForeignKey(MyRelatedModel, on_delete=models.CASCADE)
    

    【讨论】:

    • 是的,我在对另一个答案的评论中承认了这个错误。为了完整起见:假设使用ManyToManyRelation 更有意义的用例,级联删除的好方法是什么? Gabi Purcaru 已删除答案的方法是否有效?
    • 删除将在null=True上级联
    • ForeignKeys 上的级联删除行为可以用on_delete 控制(但默认为CASCADE
    【解决方案2】:

    我今天有这个确切的用例:

    • 模型作者:可以有多个条目
    • 模型条目:可以有多个作者

    为此,我使用了 ManyToManyRelationship。

    我的用例是:如果我删除了某个作者的最后一个条目,那么这个作者也应该被删除。

    可以使用pre_delete signal实现解决方案:

    @receiver(pre_delete, sender=Entry)
    def pre_delete_story(sender, instance, **kwargs):
        for author in instance.authors.all():
            if author.entries.count() == 1 and instance in author.entries.all():
                # instance is the only Entry authored by this Author, so delete it
                author.delete()
    

    【讨论】:

    • 为什么要查看and instance in authors.entries.all()?您已经在实例关系中迭代作者。
    • 这是个好问题,我不知道为什么要添加它。没有检查也可能。
    【解决方案3】:

    只需使用 clear() 方法删除相关对象,因为 Django 使用直通模型来指定关系 clear 方法删除所有相关的 BlogEntryRevision

    be = BlogEntry.objects.get(id=1)
    be.blogentryrevision_set.clear()
    

    【讨论】:

      【解决方案4】:

      您可以使用custom model manager,但文档似乎表明它是does do something like this already,我不记得这是什么意思:

      删除方法,方便的是 命名为删除()。这种方法 立即删除对象并拥有 没有返回值。示例:

      e.delete()
      

      您也可以删除对象 散装。每个 QuerySet 都有一个 delete() 方法,删除所有成员 那个查询集。

      例如,这会删除所有 Entry pub_date 年份为 2005 年的对象:

      Entry.objects.filter(pub_date__year=2005).delete()
      

      请记住,无论何时 可能,纯粹在 SQL 中执行, 所以 delete() 方法 单个对象实例不会 必须在期间调用 过程。如果您提供了自定义 模型类上的 delete() 方法和 要确保它被调用,你 将需要“手动”删除 该模型的实例(例如,通过 遍历 QuerySet 并调用 分别在每个对象上删除()) 而不是使用批量删除() QuerySet 的方法。

      当 Django 删除一个对象时,它 模拟 SQL 的行为 对 DELETE CASCADE 的约束——在 换句话说,任何具有 指向对象的外键 被删除将连同被删除 例如:

      b = Blog.objects.get(pk=1)
      # This will delete the Blog and all of its Entry objects.
      b.delete()
      

      【讨论】:

      • 对,级联删除是 1:N (ForeignKey) 关系的默认值,例如引用文档中的 Blog 1:N Entry。但是我有一个ManyToManyRelation,所以级联只删除了“条目X 和修订Y 属于一起”的记录,而不是BlogEntryRevision 记录本身。也许我应该在我的用例中使用ForeignKey 来做...无论如何,将这个问题视为关于多对多级联删除的通用问题。
      猜你喜欢
      • 2011-04-20
      • 2012-09-21
      • 2015-02-18
      • 1970-01-01
      • 2011-10-12
      • 1970-01-01
      • 2016-03-26
      • 2010-09-07
      相关资源
      最近更新 更多