You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中使用average_rating属性排序文章报错的解决方法咨询

问题描述

尝试按文章的平均评分对Article模型实例排序时触发FieldError,错误提示无法将average_rating解析为字段——因为该值是模型的@property属性,属于Python层面的计算值,无法被Django ORM直接用于数据库层面的排序。

相关代码

Models代码

class Article(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='articles')
    caption = models.CharField(max_length=250)
    
    @property
    def average_rating(self):
        return self.articlecomments.all().aggregate(Avg('rate')).get('rate__avg', 0.00)

class ArticleComment(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    article = models.ForeignKey(Article, on_delete=models.CASCADE, null=True, blank=True, related_name='articleratings')
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='articleratings_author')
    rate = models.IntegerField(default='0')

ViewSet代码

class ArticleByCategoryViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAuthenticated,)
    queryset = Article.objects.all().order_by('-average_rating')
    serializer_class = ArticleSerializer
    pagination_class = StandardResultsSetPagination

错误信息

raise FieldError("Cannot resolve keyword '%s' into field. "
django.core.exceptions.FieldError: Cannot resolve keyword 'average_rating' into field. Choices are:......
解决方案

Django ORM无法直接使用模型的@property属性进行排序,可通过以下几种方式解决:

方法1:ORM聚合查询实现数据库层面排序

直接在查询时通过ORM聚合计算平均评分,将其作为临时字段参与排序,同时该字段可直接用于后续序列化:

from django.db.models import Avg

class ArticleByCategoryViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAuthenticated,)
    queryset = Article.objects.annotate(
        average_rating=Avg('articleratings__rate')
    ).order_by('-average_rating')
    serializer_class = ArticleSerializer
    pagination_class = StandardResultsSetPagination

注意:原代码存在关联名称不匹配问题——ArticleComment的related_name是articleratings,但Article的average_rating属性中调用的是self.articlecomments.all(),需统一两者名称:要么把ArticleComment的related_name改为articlecomments,要么把模型属性中的调用改为self.articleratings.all(),否则聚合查询会报错。

若想保留@property属性,可修改为优先使用annotate生成的字段:

@property
def average_rating(self):
    if hasattr(self, 'average_rating'):
        return self.average_rating or 0.00
    return self.articleratings.all().aggregate(Avg('rate')).get('rate__avg', 0.00)

方法2:预计算存储评分(适合高并发/频繁排序场景)

若文章评分更新频率低,可在Article模型中添加存储平均评分的字段,通过信号自动更新该字段:

1. 修改模型

class Article(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='articles')
    caption = models.CharField(max_length=250)
    average_rating = models.FloatField(default=0.00)  # 新增存储评分的字段

class ArticleComment(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    article = models.ForeignKey(Article, on_delete=models.CASCADE, null=True, blank=True, related_name='articleratings')
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='articleratings_author')
    rate = models.IntegerField(default=0)  # 修正default为整数0,而非字符串'0'

2. 编写信号更新评分

from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.db.models import Avg

@receiver([post_save, post_delete], sender=ArticleComment)
def update_article_rating(sender, instance, **kwargs):
    if instance.article:
        avg_rating = instance.article.articleratings.aggregate(Avg('rate')).get('rate__avg', 0.00)
        instance.article.average_rating = avg_rating or 0.00
        instance.article.save()

3. 修改ViewSet排序

class ArticleByCategoryViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAuthenticated,)
    queryset = Article.objects.all().order_by('-average_rating')
    serializer_class = ArticleSerializer
    pagination_class = StandardResultsSetPagination

此方式排序性能更高,但需维护额外字段与信号逻辑。

方法3:内存层面排序(仅适合小数据量)

若数据量极小,可先查询所有文章,再在Python内存中排序:

class ArticleByCategoryViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAuthenticated,)
    serializer_class = ArticleSerializer
    pagination_class = StandardResultsSetPagination

    def get_queryset(self):
        articles = list(Article.objects.all())
        return sorted(articles, key=lambda x: x.average_rating, reverse=True)

注意:该方式会将所有文章加载到内存,数据量大时性能极差,且原生分页逻辑失效,需自行处理分页。


内容的提问来源于stack exchange,提问作者user14491987

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:46:34