Django中按点赞数排序文章查询集,获取Top2最受欢迎文章
需求与问题描述
现有Article、Sentiment、Reaction三个模型,其中Sentiment的id为1代表点赞,id为2代表点踩,用户对文章的互动数据存在Reaction表中,模型定义如下:
# Reaction 模型 class Reaction(models.Model): user_id = models.ForeignKey(User, related_name='user_id', on_delete=models.CASCADE) article_id = models.ForeignKey(Article, related_name='article_id', on_delete=models.CASCADE) sentiment = models.ForeignKey(Sentiment, related_name='sentiment', on_delete=models.CASCADE)
需要实现:查询点赞数最多的2篇文章,返回文章基础信息。当前编写的视图和序列化器无法正确统计点赞数并按降序排序,现有代码如下:
现有视图(views.py)
class MostPopularView(viewsets.ModelViewSet): articles = Reaction.objects.filter(sentiment=1).annotate(num_likes = Count('article_id')).order_by('num_likes')[:4] # queryset = Article.objects.filter(id=articles['article_id']) #Doesn't work by hypothetically what I'm thinking for article in articles: queryset = Article.objects.filter(id=article['article_id']) serializer_class = MostPopularSerializer
现有序列化器(serializers.py)
class MostPopularSerializer(serializers.ModelSerializer): class Meta: fields = ('id','title','tags',) model = Article
URL配置
path('popular', views.MostPopularView.as_view({'get': 'list'}))
解决方案
1. 修正视图逻辑
直接对Article模型做注解统计,过滤出点赞相关的互动记录,再按点赞数降序取前2条:
from django.db.models import Count, Q from rest_framework import viewsets class MostPopularView(viewsets.ReadOnlyModelViewSet): serializer_class = MostPopularSerializer def get_queryset(self): # 给每个文章注解点赞数,仅统计sentiment=1的Reaction return Article.objects.annotate( num_likes=Count( 'article_id', # 对应Reaction中article_id的related_name filter=Q(article_id__sentiment__id=1), distinct=False ) ).order_by('-num_likes')[:2]
- 用
ReadOnlyModelViewSet足够,因为只需要处理GET列表请求 Q对象精准过滤点赞记录,确保统计的是sentiment_id=1的互动order_by('-num_likes')是降序排序,负号不能少- 不需要循环查询,一次注解就能完成统计+排序+筛选
2. 序列化器优化(可选)
如果要返回点赞数,给序列化器新增字段:
from rest_framework import serializers from .models import Article class MostPopularSerializer(serializers.ModelSerializer): num_likes = serializers.IntegerField() # 新增点赞数字段 class Meta: fields = ('id', 'title', 'tags', 'num_likes') model = Article
3. URL配置保持不变
原URL配置无需修改:
path('popular', views.MostPopularView.as_view({'get': 'list'}))
内容的提问来源于stack exchange,提问作者user14611431
相关产品推荐
相关产品推荐

