QuerySet 的三种优雅写法
自定义 Manager、链式过滤、annotate 聚合——把查询逻辑从视图里抽出来。
QuerySet 的三种优雅写法
1. 把常用条件收进 QuerySet
视图里反复写 filter(status='published', publish_at__lte=now) 很快会失控。更好的做法是定义语义化的方法:
class PostQuerySet(models.QuerySet):
def published(self):
return self.filter(
status=Post.Status.PUBLISHED,
publish_at__lte=timezone.now(),
)
def ordered(self):
return self.order_by('-pinned', '-publish_at')
调用时就变成 Post.objects.published().ordered(),意图一眼可见。
2. 链式而不是嵌套
QuerySet 是惰性的,链式调用不会产生额外查询:
qs = Post.objects.published()
if category:
qs = qs.filter(category=category)
if query:
qs = qs.filter(Q(title__icontains=query) | Q(content__icontains=query))
比一堆 if/else 拼 SQL 字符串可读得多。
3. 用 annotate 把统计交给数据库
需要“每个分类下有几篇文章”时,不要在模板里循环查询:
Category.objects.annotate(
post_count=Count('posts', filter=Q(posts__status='published'))
)
一次查询拿到全部计数,避免 N+1。
小结
| 写法 | 收益 |
|---|---|
| 自定义 QuerySet | 语义清晰、可复用 |
| 链式过滤 | 组合灵活、惰性执行 |
| annotate | 消除 N+1、逻辑下沉 |
评论
1 条annotate 那段很实用,之前确实一直在模板里循环查询。