Rủi ro là bộ lọc lan ra những chỗ bạn không lường trước, vì manager mặc định (manager khai báo đầu tiên) được dùng ngầm ở nhiều nơi.
python
class ActiveManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(deleted_at__isnull=True)
class Post(models.Model):
objects = ActiveManager() # first manager -> becomes _default_manager
all_objects = models.Manager()Bốn hệ quả cần biết:
- Reverse relation (
author.post_set.all()) dùng subclass của manager mặc định của model liên quan, nên cũng bị lọc theo. Đôi khi đúng ý, đôi khi làm mất dữ liệu một cách âm thầm. - Django Admin dùng
_default_manager, nên bản ghi đã soft-delete sẽ biến mất khỏi admin — chính là chỗ người ta cần vào để khôi phục. Ghi đèget_queryset()củaModelAdminđể trảall_objects. - Cascade delete và ràng buộc khoá ngoại dùng
_base_manager(manager thô, không lọc), nên vẫn thấy đầy đủ dòng — hành vi này đúng, nhưng khiếnobjects.count()và số dòng thực tế trong bảng lệch nhau. - Migration lịch sử không mang theo custom manager trừ khi khai báo
use_in_migrations = True.
Cách viết ít bất ngờ hơn: đặt manager thô làm mặc định, còn bộ lọc để ở manager thứ hai — người đọc phải nói rõ ý định.
python
class PostQuerySet(models.QuerySet):
def active(self):
return self.filter(deleted_at__isnull=True)
class Post(models.Model):
objects = PostQuerySet.as_manager() # unfiltered default
Post.objects.active() # explicit intent, chainableQuerySet.as_manager() giữ được tính nối chuỗi (.active().filter(...)) mà manager thuần không có, và không giấu bộ lọc sau lưng người dùng.