You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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,提问作者김대휘

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 09:05:33