当没有更多关系时,django 删除 m2m 实例

django remove m2m instance when there are no more relations

如果我们有模型:

class Publication(models.Model):
    title = models.CharField(max_length=30)

class Article(models.Model):
    publications = models.ManyToManyField(Publication)

根据:https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_many/,要创建一个对象,我们必须先保存两个对象,然后才能创建关系:

p1 = Publication(title='The Python Journal')
p1.save()
a1 = Article(headline='Django lets you build web apps easily')
a1.save()
a1.publications.add(p1)

现在,如果我们在其中任何一个对象中调用 delete,该对象以及两个对象之间的关系将从数据库中删除。到这里为止我明白了。

但是有没有办法做到这一点,如果删除 Article,那么,与任何 Article 无关的所有 Publications 都将从数据库中删除也?或者实现这一目标的唯一方法是首先查询所有文章,然后像这样遍历它们:

to_delete = []
qset = a1.publications.all()
for publication in qset:
    if publication.article_set.count() == 1:
        to_delete(publication.id)
a1.delete()
Publications.filter(id__in=to_delete).delete()

但这有很多问题,特别是并发问题,因为可能 publication 在调用 .count() 和 [=20] 之间被另一个 article 使用=].

有什么方法可以自动执行此操作,例如在创建模型时执行“条件”on_delete=models.CASCADE 或其他操作?

谢谢!

您可以使用此查询删除相关对象:

a1.publications.annotate(article_count=Count('article_set')).filter(article_count=1).delete()

annotate 为查询集(别名字段)创建一个临时字段,它为 Publication 对象的查询集中的每个实例聚合许多相关的 Article 对象,使用 Count 函数。 Count 是任何 SQL 中的 built-in 聚合函数,其中 returns 来自查询的行数(在本例中为多个相关实例)。然后,我们过滤掉那些 article_count 等于 1 的结果并删除它们。

我尝试使用@Ersain 回答:

a1.publications.annotate(article_count=Count('article_set')).filter(article_count=1).delete()

无法正常工作。首先,我在关系中找不到 article_set 变量。

django.core.exceptions.FieldError: Cannot resolve keyword 'article_set' into field. Choices are: article, id, title

然后,运行 QuerySet 上的计数过滤器在按文章过滤后返回了文章中的所有标签,而不仅仅是带有 article_count=1 的标签。所以最后这是我设法让它工作的代码:

Publication.objects.annotate(article_count=Count('article')).filter(article_count=1).filter(article=a1).delete()

我当然不是专家,不确定这是否是最好的方法,也不确定它是否真的很费时,所以我愿意接受建议。但截至目前,这是我发现的唯一以原子方式执行此操作的解决方案。