Django查询用户发文关联分类的实现与性能优化
Django ORM优化:高效获取用户文章关联的去重分类
现有实现的性能缺陷
当前写法存在两个明显的性能问题:
- 产生N+1查询:首先执行1次SQL拉取用户所有文章的分类ID到Python内存,再对每个去重后的分类ID单独执行1次查询获取分类对象,分类数量越多,查询次数越多。
- 不必要的内存开销:查询分类ID时会拉取所有重复值(比如用户10篇文章都属于同一个分类,会返回10个重复ID),再在Python层转集合去重,当用户发布文章量很大时,会占用多余内存。
优化实现
直接通过Category模型做反向关联查询,在数据库层完成去重,仅需1条SQL即可拿到全部需要的分类对象,完全避免Python层去重和N+1问题,修改get_context_data方法如下:
def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) c_form = CommentModelForm(self.request.POST or None) target_username = self.kwargs.get('username') # 数据库层直接去重查询关联分类,无额外循环查询 users_categories = Category.objects.filter( post__author__username=target_username ).distinct() context['users_categories'] = users_categories return context
实现原理
- 利用Django ORM跨关联查询的能力,直接筛选出「被目标用户发布的文章关联」的分类条目,调用
distinct()时会在生成的SQL中加上DISTINCT关键字,由数据库完成去重,不需要把原始重复数据拉到应用层处理。 - 该写法天然排除了
category=None的空值场景,因为空分类不会在Category表中有对应记录,不需要额外加exclude判断。 - 对应生成的SQL参考:
SELECT DISTINCT blog_category.* FROM blog_category INNER JOIN blog_post ON blog_category.id = blog_post.category_id INNER JOIN auth_user ON blog_post.author_id = auth_user.id WHERE auth_user.username = '传入的用户名参数'
可选附加优化
如果模板中需要遍历文章列表访问每篇文章的分类、作者字段,可以在原有get_queryset方法中加上select_related预加载外键关联对象,避免遍历文章时产生额外的外键查询:
def get_queryset(self): return ( super() .get_queryset() .filter(author__username=self.kwargs.get('username')).order_by('-date_posted') # 预加载外键关联对象,消除遍历文章时的外键查询 .select_related('category', 'author') .prefetch_related( Prefetch( "comment_set", queryset=Comment.objects.annotate( like_count=Count("liked") ).order_by("-like_count"), to_attr="comment_list", ) ) )
内容的提问来源于stack exchange,提问作者ckp7blessed
相关产品推荐
相关产品推荐

