软删除最常见的写法是覆盖 Manager 的 get_queryset。它挡得住多少?为什么 book.author 还是能拿到「已删除」的作者?这篇用一个带软删除的小模型逐项验证。
Book.objects.cheap() → ['b1', 'gb']
Book.objects.filter(...).cheap() → ['b1', 'gb']
Book.objects.by("ann").cheap() → ['b1']
类型: ManagerFromBookQuerySet / BookQuerySet把可复用的过滤写成 QuerySet 的方法,再 as_manager(),既能 Book.objects.cheap(),也能 Book.objects.filter(...).cheap()。直接写 Manager 方法则没法链在 filter 后面。
Author.objects.all() → ['ann'] Author.all_objects.all() → ['ann', 'ghost'] Author._default_manager 是 ActiveManager (模型里第一个定义的 Manager) Author._base_manager 是 Manager Author.objects.get(name='ghost') → DoesNotExist
把「排除已删除」放进默认 Manager,业务代码里 Author.objects 就天然看不到它们。同时保留一个不过滤的 Manager 供管理用。
gb.author → ghost deleted = True (关联对象访问使用 _base_manager,不受 objects 过滤) Book.objects.filter(author__name="ghost") → ['gb'] (JOIN 也不受影响) ann.books.all() → ['b1', 'b2'] related manager 的类: RelatedManager
外键的正向访问(book.author)使用 _base_manager,不受你自定义的默认 Manager 影响——这是有意的(否则外键会「悬空」)。JOIN、filter(author__…) 也一样。如果业务要求「已删除的作者不应出现」,要在这些地方显式处理。
Author._meta.managers 顺序: ['objects', 'all_objects'] Book._meta.managers 顺序 : ['objects']
第一个定义的 Manager 是默认 Manager(_default_manager),admin、序列化等许多内部功能用它。把「过滤后的」放第一位要想清楚这些内部用途(本文没有验证 admin 的行为)。