如何在Django查询中为用户添加最喜爱文章分类统计?
扩展Django查询:获取用户点赞总数及最喜爱文章分类
我来帮你搞定这个需求!下面先梳理下现有基础,再给出两种可行的实现方案——一种是基于你现有raw SQL的扩展,另一种是更符合Django风格的ORM实现。
现有模型结构
首先回顾下你的Django模型(修正了笔误的DateBaseModel):
from django.db import models class ArticleCategory(models.Model): name = models.CharField(('Name'), max_length=255) class Article(models.Model): category = models.ForeignKey('articles.ArticleCategory', null=True, blank=True, related_name='articles', on_delete=models.SET_NULL) class ArticleLike(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, related_name='user_likes') article = models.ForeignKey('articles.Article', on_delete=models.CASCADE, related_name='article_likes')
现有查询逻辑
你当前用raw SQL获取用户的点赞总数:
from django.contrib.auth import get_user_model User = get_user_model() condition = """ SELECT ( SELECT COUNT(articles_articlelike.id) FROM articles_articlelike WHERE articles_articlelike.user_id = account_account.id ) AS "likes_count", "account_account"."id" FROM "account_account" """ users = User.objects.raw(condition)
方案一:扩展原有Raw SQL
我们需要关联点赞记录→文章→分类,统计每个用户各分类的点赞次数,取最高的那个分类的ID和名称。修改后的SQL如下:
SELECT -- 原有点赞总数统计 (SELECT COUNT(al.id) FROM articles_articlelike al WHERE al.user_id = account_account.id) AS "likes_count", -- 获取点赞最多的分类名称(无点赞时返回'无') COALESCE( (SELECT ac.name FROM articles_articlelike al JOIN articles_article a ON al.article_id = a.id JOIN articles_articlecategory ac ON a.category_id = ac.id WHERE al.user_id = account_account.id GROUP BY ac.id, ac.name ORDER BY COUNT(al.id) DESC, ac.name ASC -- 点赞数相同时按分类名排序,保证结果稳定 LIMIT 1), '无' ) AS "favorite_category_name", -- 获取点赞最多的分类ID(无点赞时返回NULL) (SELECT ac.id FROM articles_articlelike al JOIN articles_article a ON al.article_id = a.id JOIN articles_articlecategory ac ON a.category_id = ac.id WHERE al.user_id = account_account.id GROUP BY ac.id, ac.name ORDER BY COUNT(al.id) DESC, ac.name ASC LIMIT 1) AS "favorite_category_id", account_account.id FROM account_account
逻辑说明:
- 通过JOIN串联
ArticleLike→Article→ArticleCategory,关联用户的点赞记录到对应的分类 - 按分类分组统计点赞数,降序排序后取第一条,就是用户点赞最多的分类
- 用
COALESCE处理无点赞的用户,返回友好的默认值 - 额外加上
ac.name ASC排序,避免多个分类点赞数相同时结果随机
方案二:用Django ORM实现(更推荐)
如果不想写原生SQL,可以用Django的annotate和Subquery来实现,更符合Django的开发习惯,也能自动适配表名变化:
from django.contrib.auth import get_user_model from django.db.models import Count, Subquery, OuterRef from articles.models import ArticleCategory User = get_user_model() # 子查询:获取当前用户点赞最多的分类名称 favorite_category_name_subquery = ArticleCategory.objects.filter( # 通过反向关联找到用户点赞的文章对应的分类 articles__article_likes__user=OuterRef('pk') ).annotate( # 统计该分类下用户的点赞次数 like_count=Count('articles__article_likes') ).order_by( '-like_count', 'name' # 按点赞数降序,名称升序 ).values('name')[:1] # 只取第一条 # 子查询:获取当前用户点赞最多的分类ID favorite_category_id_subquery = ArticleCategory.objects.filter( articles__article_likes__user=OuterRef('pk') ).annotate( like_count=Count('articles__article_likes') ).order_by( '-like_count', 'name' ).values('id')[:1] # 最终查询:获取用户ID、点赞总数、最喜爱分类 users = User.objects.annotate( # 统计用户总点赞数 likes_count=Count('user_likes'), # 关联子查询获取分类信息 favorite_category_name=Subquery(favorite_category_name_subquery), favorite_category_id=Subquery(favorite_category_id_subquery) ).values('id', 'likes_count', 'favorite_category_name', 'favorite_category_id')
优势:
- 无需硬编码表名,Django会自动处理
- 代码可读性更强,符合ORM的设计思想
- 方便后续扩展或修改查询条件
注意事项
- 如果用户有多个分类点赞数相同且都是最大值,两种方案都会取排序后的第一个(我们加了
name ASC来保证结果稳定) - 对于没有任何点赞的用户,
likes_count会是0,favorite_category_name会是None(SQL方案中我们用COALESCE改成了'无',ORM方案可以在后续处理中补充)
内容的提问来源于stack exchange,提问作者Sandeep Balagopal
相关产品推荐
相关产品推荐

