如何用Django软删除多对多的关系

Mar*_*tin 6 python django django-models

在我的Django项目中,必须通过将当前日期时间设置为deleted_at属性来软删除用户删除的所有实体.我的模型如下所示:Trip < - > TripDestination < - > Destination(多对多关系).换句话说,Trip可以有多个目的地.

当我删除Trip时,SoftDeleteManager会过滤掉所有已删除的行程.但是,如果我请求旅行的所有目的地(使用get_object_or_404(Trip,pk = id)),我也会得到删除的目的地(即具有deleted_at == null或者deleted_at!= null的TripDestination模型).我真的不明白为什么我的所有模型都继承自LifeTimeTracking并使用SoftDeleteManager.

有人可以帮我理解为什么SoftDeleteManager不能用于n:m关系吗?

class SoftDeleteManager(models.Manager):
    def get_query_set(self):
        query_set = super(SoftDeleteManager, self).get_query_set()
        return query_set.filter(deleted_at__isnull = True)

class LifeTimeTrackingModel(models.Model):
    created_at = models.DateTimeField(auto_now_add = True)
    updated_at = models.DateTimeField(auto_now = True)
    deleted_at = models.DateTimeField(null = True)

    objects = SoftDeleteManager()
    all_objects = models.Manager()

    class Meta:
        abstract = True

class Destination(LifeTimeTrackingModel):
    city_name = models.CharField(max_length = 45)

class Trip(LifeTimeTrackingModel):
    name = models.CharField(max_length = 250)
    destinations = models.ManyToManyField(Destination, through = 'TripDestination')

class TripDestination(LifeTimeTrackingModel):
    trip = models.ForeignKey(Trip)
    destination = models.ForeignKey(Destination)
Run Code Online (Sandbox Code Playgroud)

第一项决议在Django Bug DB中 提交了错误17746.感谢Caspar对此的帮助.

Cas*_*par 2

看起来这种行为来自 ManyToManyField 选择使用自己的管理器,相关对象参考提到了这一点,因为当我尝试构建一些我自己的实例并尝试使用您的模型代码软删除它们时(通过manage.py) shell)一切都按预期进行。

不幸的是,它没有提到如何覆盖模型管理器。我花了大约 15 分钟搜索 ManyToManyField 源代码,但没有找到它实例化管理器的位置(查看 django/db/models/fields/lated.py)。

为了获得您想要的行为,您应该按照控制自动管理器的文档指定use_for_related_fields = True您的类:SoftDeleteManager

class SoftDeleteManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        query_set = super(SoftDeleteManager, self).get_query_set()
        return query_set.filter(deleted_at__isnull = True)
Run Code Online (Sandbox Code Playgroud)

这按预期工作:我可以Trip用 2 Destinations 定义 a ,每个都通过 a TripDestination,如果我将 aDestinationdeleted_at值设置为datetime.datetime.now()Destination不再出现在 给出的列表中mytrip.destinations.all(),这就是您所追求的告诉。

但是,文档还特别指出不要通过覆盖get_query_set()用于相关字段的管理器来过滤查询集,因此如果您以后遇到问题,请记住这一点作为可能的原因。