如何使用SafeDeleteModel删除多对多关联对象?
问题描述
使用django-safedelete库为所有模型设置了SOFT_DELETE_CASCADE软删除级联策略,但执行代码移除多对多关联对象时,该对象并未从集合中消失。相关代码及操作如下:
模型代码(models.py)
from django.db import models from safedelete import SOFT_DELETE_CASCADE, HARD_DELETE from safedelete.models import SafeDeleteModel class Post(SafeDeleteModel): _safedelete_policy: int = SOFT_DELETE_CASCADE category = models.CharField("카테고리", max_length=100, null=True, blank=True) title = models.CharField("제목", max_length=300, null=True, blank=True) created_at = models.DateTimeField("생성일시", auto_now_add=True) updated_at = models.DateTimeField("수정시간", auto_now=True) tags = models.ManyToManyField("Tag", through="TagPostAssociation", related_name="posts") def __str__(self): return f"글(ID:{self.id}/제목:{self.title})" class Tag(SafeDeleteModel): _safedelete_policy: int = SOFT_DELETE_CASCADE value = models.CharField("태그 값", max_length=100) created_at = models.DateTimeField("생성일시", auto_now_add=True) updated_at = models.DateTimeField("수정시간", auto_now=True) class Meta: verbose_name = verbose_name_plural = "태그" def __str__(self): return f"태그(ID:{self.id}/값:{self.value})" class TagPostAssociation(SafeDeleteModel): _safedelete_policy: int = HARD_DELETE tag = models.ForeignKey(to=Tag, verbose_name="태그", on_delete=models.CASCADE) post = models.ForeignKey(to=Post, verbose_name="글", on_delete=models.CASCADE) created_at = models.DateTimeField("생성일시", auto_now_add=True) updated_at = models.DateTimeField("수정시간", auto_now=True) class Meta: verbose_name = verbose_name_plural = "글-태그" def __str__(self): return f"글{self.post.id})-태그({self.tag.id})"
命令行操作
>>> post = Post.objects.get(id=2) >>> tag = Tag.objects.get(id=2) >>> post.tags.all() <SafeDeleteQueryset [<Tag: 태그(ID:2/값:hair)>, <Tag: 태그(ID:1/값:test)>]> >>> post.tags.remove(tag) >>> post.tags.all() <SafeDeleteQueryset [<Tag: 태그(ID:2/값:hair)>, <Tag: 태그(ID:1/값:test)>]>
尝试使用m2m_changed信号处理删除逻辑,但未生效,寻求解决办法。
解决方案
1. 直接操作关联中间表
由于使用了自定义的多对多关联模型(TagPostAssociation),Django 原生的remove()、add()等方法无法直接修改关联关系(这类方法仅适用于无自定义中间表的多对多场景)。需要直接删除中间表的对应记录:
# 删除指定post和tag的关联实例 TagPostAssociation.objects.filter(post=post, tag=tag).delete()
执行后如果需要立即刷新当前post对象的关联缓存,可重新获取实例:
post = Post.objects.get(id=post.id) post.tags.all() # 此时已看不到被移除的标签
2. 关于m2m_changed信号失效的原因
m2m_changed信号仅在 Django 原生多对多操作(如add()/remove())触发时发送,但因为自定义了中间表,这些原生方法本身不会执行任何逻辑,因此信号也不会被触发。若需要监听关联关系变化,可改用post_delete或post_save信号监听TagPostAssociation模型的操作。
3. 调整关联关系的删除策略
当前TagPostAssociation使用的是HARD_DELETE策略,执行delete()后会直接从数据库移除记录。如果需要对关联关系也使用软删除,只需将其_safedelete_policy改为SOFT_DELETE_CASCADE即可:
class TagPostAssociation(SafeDeleteModel): _safedelete_policy: int = SOFT_DELETE_CASCADE # 其余字段不变
此时软删除的关联记录可通过TagPostAssociation.objects.all(force_visibility=True)查看。
内容的提问来源于stack exchange,提问作者김대휘
相关产品推荐
相关产品推荐

