当没有更多关系时,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()
我当然不是专家,不确定这是否是最好的方法,也不确定它是否真的很费时,所以我愿意接受建议。但截至目前,这是我发现的唯一以原子方式执行此操作的解决方案。
如果我们有模型:
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()
我当然不是专家,不确定这是否是最好的方法,也不确定它是否真的很费时,所以我愿意接受建议。但截至目前,这是我发现的唯一以原子方式执行此操作的解决方案。